隐藏真实的xxx.github.io

前言 在免费使用 GitHub 私有仓库部署网站一文介绍了如何部署github pages,并实现了自定义域名的配置。本文介绍另一种自定义域名来访问github pages的方法, 特点是: 访客无法通过user.me 追踪到真实的user.github.io 通过cloudflare worker来实现 user.me 指向 user.github.io 整个访问流程如下: 用户访问 user.me ↓ Cloudflare DNS (指向 Worker) ↓ Worker 处理请求 (隐藏源站) ↓ Worker 访问 user.github.io ↓ GitHub Pages 返回内容 这种方案的优势: 安全性更高:完全隐藏GitHub Pages源站 配置简单:无需复杂的DNS设置 更少出错:避免DNS解析冲突 维护方便:集中化管理 注意:使用这种方法时,GitHub Pages只需保持默认的xxx.github.io域名即可,无需配置CNAME文件。 具体实现 1. 创建cf worker 首先创建一个名为hide-ghpage的Worker, 在设置中添加环境变量: GITHUB_PAGES 填写 xxx.github.io CUSTOM_DOMAIN 填写 user.me WWW_DOMAIN 填写 www.user.me WORKER_DOMAIN 填写 hide-ghpage.user.workers.dev 然后编辑代码如下: 点击查看 ▼ function encodePathProperly(path) { // 先解码以防重复编码 const decodedPath = decodeURIComponent(path); // 替换特殊字符,之前被这个坑惨了。由于网页中包含邮件地址,特殊字符(如 @)在 URL 中无法正确处理,一直显示404. return decodedPath .split('/') .map(segment => segment .replace(/@/g, '%40') // 替换@ .replace(/#/g, '%23') .replace(/\?/g, '%3F') .replace(/\[/g, '%5B') .replace(/\]/g, '%5D') .replace(/=/g, '%3D') .replace(/&/g, '%26') ) .join('/'); } function initializeConfig(env) { console.log('Initializing with env vars:', { GITHUB_PAGES: env.GITHUB_PAGES, CUSTOM_DOMAIN: env.CUSTOM_DOMAIN, WWW_DOMAIN: env.WWW_DOMAIN, WORKER_DOMAIN: env.WORKER_DOMAIN }); const DOMAINS = { GITHUB: env.GITHUB_PAGES, CUSTOM: env.CUSTOM_DOMAIN, WWW: env.WWW_DOMAIN, WORKER: env.WORKER_DOMAIN }; return { domains: DOMAINS, githubPages: DOMAINS.GITHUB, allowedDomains: [DOMAINS.CUSTOM, DOMAINS.WWW, DOMAINS.WORKER], sensitiveHeaders: [ 'server', 'x-powered-by', 'x-github-request-id', 'x-fastly', 'x-proxy-cache', 'via', 'x-served-by', 'x-cache', 'x-cache-hits', 'x-timer', 'x-fastly-request-id', 'x-request-id', 'x-runtime', 'x-robots-tag', 'alt-svc', 'x-github', 'x-origin-cache', 'x-content-type-options' ], customHeaders: { 'server': 'cloudflare', 'X-Frame-Options': 'SAMEORIGIN', 'X-Content-Type-Options': 'nosniff', 'Referrer-Policy': 'strict-origin-when-cross-origin', 'Permissions-Policy': 'interest-cohort=()', 'Content-Security-Policy': "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';", 'X-Powered-By': 'Cloudflare' }, debug: true }; } async function logError(error, request, extra = {}) { console.error('Error:', { message: error.message, stack: error.stack, url: request.url, method: request.method, ...extra }); } async function handleRequest(request, env) { const config = initializeConfig({ GITHUB_PAGES: env.GITHUB_PAGES, CUSTOM_DOMAIN: env.CUSTOM_DOMAIN, WWW_DOMAIN: env.WWW_DOMAIN, WORKER_DOMAIN: env.WORKER_DOMAIN }); const { domains } = config; try { const url = new URL(request.url); const currentDomain = url.hostname; // URL 编码处理 const encodedPath = encodePathProperly(url.pathname); console.log('URL encoding:', { originalPath: url.pathname, encodedPath: encodedPath }); // 详细的请求日志 console.log('Request details:', { url: url.toString(), domain: currentDomain, path: encodedPath, allowedDomains: config.allowedDomains, method: request.method, headers: Object.fromEntries(request.headers) }); // www 重定向到主域名 if (currentDomain === domains.WWW) { const newUrl = `https://${domains.CUSTOM}${encodedPath}${url.search}`; console.log('Redirecting www to non-www:', { from: url.toString(), to: newUrl }); return new Response(null, { status: 301, headers: { 'Location': newUrl, 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'server': 'cloudflare' } }); } // 验证域名 if (!config.allowedDomains.includes(currentDomain)) { console.warn('Invalid domain access:', { domain: currentDomain, allowedDomains: config.allowedDomains }); return new Response('Domain not allowed', { status: 403 }); } // 构建 GitHub URL const githubUrl = `https://${config.githubPages}${encodedPath}${url.search}`; console.log('Proxying to GitHub:', { from: url.toString(), to: githubUrl }); // 获取文件扩展名 const ext = encodedPath.split('.').pop().toLowerCase(); const isAsset = ['png', 'jpg', 'jpeg', 'gif', 'webp', 'css', 'js', 'svg', 'ico', 'woff', 'woff2', 'ttf'].includes(ext); // 处理请求头 let newHeaders = new Headers(request.headers); // 清理敏感请求头 ['referer', 'origin', 'x-real-ip', 'cf-connecting-ip', 'x-forwarded-for', 'x-forwarded-proto', 'cookie'].forEach(header => { newHeaders.delete(header); }); // 设置新的请求头 newHeaders.set('Host', config.githubPages); newHeaders.set('Accept-Encoding', 'gzip, deflate, br'); newHeaders.set('User-Agent', request.headers.get('User-Agent') || 'Cloudflare Worker'); // 创建 GitHub 请求 const githubRequest = new Request(githubUrl, { method: request.method, headers: newHeaders, redirect: isAsset ? 'follow' : 'manual', cf: { cacheEverything: false, cacheTtl: isAsset ? 86400 : 0, timeout: 30000 } }); // 发送请求到 GitHub Pages let response = await fetch(githubRequest); console.log('GitHub response:', { status: response.status, statusText: response.statusText, headers: Object.fromEntries(response.headers), url: githubUrl }); // 如果是 404,增加调试日志 if (response.status === 404) { console.log('404 Error details:', { githubUrl, originalUrl: request.url, headers: Object.fromEntries(response.headers), domain: currentDomain, githubPages: config.githubPages }); // 尝试直接访问 GitHub Pages try { const testResponse = await fetch(`https://${config.githubPages}${encodedPath}`); console.log('Direct GitHub test:', { status: testResponse.status, headers: Object.fromEntries(testResponse.headers) }); } catch (error) { console.error('Direct GitHub test failed:', error); } } // 处理资源文件 if (isAsset) { let newResponse = new Response(response.body, { status: response.status, statusText: response.statusText, headers: new Headers(response.headers) }); const allowedHeaders = [ 'content-type', 'content-length', 'cache-control', 'content-encoding', 'etag', 'last-modified' ]; for (const key of [...newResponse.headers.keys()]) { if (!allowedHeaders.includes(key.toLowerCase())) { newResponse.headers.delete(key); } } if (response.ok) { newResponse.headers.set('Cache-Control', 'public, max-age=31536000'); } return newResponse; } // 处理重定向 if ([301, 302, 307, 308].includes(response.status)) { const location = response.headers.get('Location'); console.log('Handling redirect:', { from: url.toString(), location: location }); if (location) { const redirectUrl = new URL(location.startsWith('http') ? location : `https://${config.githubPages}${location}`); const newLocation = `https://${currentDomain}${redirectUrl.pathname}${redirectUrl.search}`; console.log('New redirect location:', newLocation); return new Response(null, { status: response.status, headers: { 'Location': newLocation, 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'server': 'cloudflare' } }); } } // 读取响应体 let body = await response.text(); // 替换规则 const replacements = [ [new RegExp(`https?://${config.githubPages}`, 'g'), `https://${currentDomain}`], [`${config.githubPages}`, currentDomain], [/href="\//g, `href="https://${currentDomain}/`], [/src="\//g, `src="https://${currentDomain}/`], [/src="([^"]+)"/g, (match, p1) => { if (p1.startsWith('http')) { return match.replace(config.githubPages, currentDomain); } else if (p1.startsWith('/')) { return `src="https://${currentDomain}${p1}"`; } return match; }], [/srcset="([^ "]+)"/g, (match, p1) => { return `srcset="${p1.split(' , ').map(src =>, size] =, currentDomain)} ${size, ')} "`; }], [/<meta[^>]*?content="[^"]*?github\.io[^"]*?"[^>]*?>/g, (match) => { return match.replace(config.githubPages, currentDomain); }], [/<link[^>]*?rel="canonical"[^>]*?href="[^"]*?"[^>]*?>/g, (match) => { return match.replace(config.githubPages, currentDomain); }], [/<script type="application\/ld\+json">[^<]*?<\/script>/g, (match) => { return match.replace(config.githubPages, currentDomain); }] ]; // 应用替换规则 replacements.forEach(([pattern, replacement]) => { body = body.replace(pattern, replacement); }); // 创建最终响应 let newResponse = new Response(body, { status: response.status, statusText: response.statusText, headers: new Headers({ 'Content-Type': response.headers.get('Content-Type') || 'text/html;charset=UTF-8', 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0' }) }); // 添加安全头部 config.sensitiveHeaders.forEach(header => { newResponse.headers.delete(header); }); Object.entries(config.customHeaders).forEach(([key, value]) => { newResponse.headers.set(key, value); }); return newResponse; } catch (err) { await logError(err, request); return new Response(`Error: ${err.message}`, { status: 500, headers: { 'Content-Type': 'text/plain', 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'server': 'cloudflare' } }); } } export default { async fetch(request, env, ctx) { // 记录环境变量状态 console.log('Environment variables:', { GITHUB_PAGES: env.GITHUB_PAGES, CUSTOM_DOMAIN: env.CUSTOM_DOMAIN, WWW_DOMAIN: env.WWW_DOMAIN, WORKER_DOMAIN: env.WORKER_DOMAIN }); try { // 记录请求信息 console.log('Incoming request:', { url: request.url, method: request.method, headers: Object.fromEntries(request.headers) }); const response = await handleRequest(request, env); // 记录响应信息 console.log('Response details:', { status: response.status, headers: Object.fromEntries(response.headers) }); return response; } catch (error) { console.error('Fatal error:', error); return new Response('Internal Server Error', { status: 500 }); } } }; 2. 配置DNS解析 要将自定义域名指向 Worker,需要完成以下配置: ...

November 13, 2024 · 1051 words · Shawn

免费使用 GitHub 私有仓库部署网站

前言 本教程详细说明如何使用 Hugo + GitHub Pages + Paper Mod 主题来部署网站,通过私有仓库管理源码,公开仓库发布网站。 最终仓库结构如下所示: 私有仓库 (hugo-source) ├── content/ │ └── posts/ │ └── hello-world.md # 原始文章 ├── themes/ ├── hugo.yaml └── public/ # 这个文件夹是一个独立的 git 仓库 ├── index.html # 构建后的文件 ├── posts/ │ └── hello-world/ │ └── index.html # 构建后的文章 └── ... 公开仓库 (username.github.io) ├── index.html # 与 public 文件夹中的文件相同 ├── posts/ │ └── hello-world/ │ └── index.html └── ... 步骤 1. 创建两个 GitHub 仓库 - 私有仓库:hugo-source (用于存放源码) - 公开仓库:username.github.io (用于发布网站) 2. 安装必要工具 # 安装 Hugo (以 Ubuntu 为例) # sudo apt install hugo # 安装 Hugo (以 Mac 为例) brew install hugo # 验证安装 hugo version 3. 创建新的 Hugo 站点 # 创建新站点, --format yaml 指定hugo 配置文件使用 yaml 格式,默认是toml格式 hugo new site /Users/yourname/Documents/hugo-source --format yaml cd /Users/yourname/Documents/hugo-source # 初始化 git 仓库 git init 4. 安装 PaperMod 主题 git submodule add --depth=1 https://github.com/adityatelange/hugo-PaperMod.git themes/PaperMod git submodule update --init --recursive # needed when you reclone your repo (submodules may not get cloned automatically) 5. 配置 hugo.yaml 点击查看 ▼ baseURL: https://username.github.io/ languageCode: en-us title: My Blog theme: PaperMod # 基础设置 enableGitInfo: false enableEmoji: true # 支持 emoji 显示 paginate: 10 # 一页显示10篇文章 # params底下的应该是PaperMod的设置 params: homeInfoParams: Title: "👋 Welcome to My Blog" Content: "Hi, this is my blog." socialIcons: - name: "rss" url: "/index.xml" - name: "email" url: "[email protected]" - name: "github" url: "https://github.com/username" label: text: "User's Blog" icon: /favicon.ico # 导航栏显示的图标 iconHeight: 35 # 控制导航栏图标大小 assets: disableHLJS: true favicon: "/favicon.ico" favicon16x16: "/favicon-16x16.png" favicon32x32: "/favicon-32x32.png" # apple_touch_icon: "/apple-touch-icon.png" # android_chrome_192: "/android-chrome-192x192.png" # android_chrome_512: "/android-chrome-512x512.png" env: production defaultTheme: auto disableSpecial1stPost: true ShowRssButtonInSectionTermList: true ShowToc: true # 文章设置 ShowReadingTime: false # 显示阅读时间 ShowPostNavLinks: true ShowBreadCrumbs: true ShowCodeCopyButtons: true ShowWordCount: true # 显示字数统计 ShowAuthor: true # 显示作者 menu: main: - name: Posts url: /posts/ weight: 1 - name: Archive url: /archive/ weight: 2 - name: Search url: /search/ weight: 3 - name: Tags url: /tags/ weight: 4 # 输出设置 outputs: home: - HTML - RSS - JSON # 用于搜索功能 section: - HTML # 文章页面设置 permalinks: posts: /posts/:year-:month-:day-:filename/ # Markdown 渲染设置 markup: goldmark: renderer: unsafe: true 6. 配置搜索和归档功能 为了确保搜索功能正常工作,创建 content/search.md: ...

November 10, 2024 · 1076 words · Shawn