如何使用 Cloudflare 搭建 GitHub 代理(国内可用)

站长文章17小时前发布 小编
2 0 0

最近有不少朋友在问:GitHub 访问慢、下载卡顿怎么办?其实有个免费又靠谱的方案——用 Cloudflare Workers 自建一个反向代理。部署起来不难,还能“白嫖”全球 CDN 加速。不过有个前提:Workers 默认分配的子域名在国内已经被墙了,所以必须绑定自己的域名才能正常使用。

下面我就把整个流程拆解清楚,从域名准备到代码部署,一步步带你搞定。


为什么需要这个代理?

GitHub 上很多资源(比如仓库里的 raw 文件、release 包、gist 等)在国内访问经常超时甚至打不开。虽然有 jsDelivr、CNPMJS 这类镜像站,但它们覆盖不全,更新也有延迟。

ad

站长导航

优网导航旗下汇集全网优质站长工具,一站式站长工具大全导航网站

而通过 Cloudflare Workers 自建代理,相当于让请求先绕道 Cloudflare 的边缘节点,再转发到 GitHub 官方服务器。借助 Cloudflare 全球网络,速度通常会有明显提升,而且完全免费。


第一步:准备一个域名

Workers 默认给的 xxx.workers.dev 域名在国内基本没法用,所以我们得自己搞个域名。

选项一:花钱买(推荐)

  • 在阿里云、腾讯云等平台注册 .top.xyz 等低价后缀域名,一年大概 30 块左右。
  • 便宜又稳定,适合长期使用。

选项二:免费白嫖(临时用)

  • 可以去 Freenom 申请免费域名(比如 .tk.ml 等)。
  • 虽然免费,但需要每年手动续期,而且部分运营商可能解析不稳定。

⚠️ 注意:无论哪种方式,记得把域名托管到 Cloudflare,这样后续才能直接在面板里配置 Workers 和自动申请 SSL 证书。


第二步:创建 Cloudflare Worker

1.登录 Cloudflare 控制台,进入 Workers & Pages 页面。

ad

AI 工具导航

优网导航旗下AI工具导航,精选全球千款优质 AI 工具集

2.点击 Create application → 选择 Worker

如何使用 Cloudflare 搭建 GitHub 代理(国内可用)

3.给你的 Worker 起个名字,比如git或 github-proxy,然后点 Deploy

如何使用 Cloudflare 搭建 GitHub 代理(国内可用)

4.部署完成后,点击进入 Quick Edit(快速编辑)界面。

如何使用 Cloudflare 搭建 GitHub 代理(国内可用)

这时候你会看到一段默认代码。别慌,我们直接替换成下面这段专为 GitHub 优化的代理脚本。

'use strict'
/**
 * static files (404.html, sw.js, conf.js)
 */
const ASSET_URL = 'https://github.com/'
// 如果你打算用 example.com/gh/ 这样的路径访问,请把 PREFIX 改成 '/gh/'
// 注意:前后斜杠不能少,否则会出错!
const PREFIX = '/'
const Config = {
  jsdelivr: 0,  // 是否启用 jsDelivr 加速 blob 文件(1 启用,0 禁用)
  cnpmjs: 0     // 是否对 git-info 类请求走 cnpmjs.org(1 启用,0 禁用)
}

/** @type {RequestInit} */
const PREFLIGHT_INIT = {
  status: 204,
  headers: new Headers({
    'access-control-allow-origin': '*',
    'access-control-allow-methods': 'GET,POST,PUT,PATCH,TRACE,DELETE,HEAD,OPTIONS',
    'access-control-max-age': '1728000',
  }),
}

// 匹配各类 GitHub 资源的正则表达式
const exp1 = /^(?:https?:\/\/)?github\.com\/.+?\/.+?\/(?:releases|archive)\/.*$/i
const exp2 = /^(?:https?:\/\/)?github\.com\/.+?\/.+?\/(?:blob|raw)\/.*$/i
const exp3 = /^(?:https?:\/\/)?github\.com\/.+?\/.+?\/(?:info|git-).*$/i
const exp4 = /^(?:https?:\/\/)?raw\.(?:githubusercontent|github)\.com\/.+?\/.+?\/.+?\/.+$/i
const exp5 = /^(?:https?:\/\/)?gist\.(?:githubusercontent|github)\.com\/.+?\/.+?\/.+$/i

/**
 * 构造响应对象
 * @param {any} body
 * @param {number} status
 * @param {Object<string, string>} headers
 */
function makeRes(body, status = 200, headers = {}) {
  headers['access-control-allow-origin'] = '*'
  return new Response(body, {status, headers})
}

/**
 * 安全地创建 URL 对象
 * @param {string} urlStr
 */
function newUrl(urlStr) {
  try {
    return new URL(urlStr)
  } catch (err) {
    return null
  }
}

addEventListener('fetch', e => {
  const ret = fetchHandler(e)
    .catch(err => makeRes('cfworker error:\n' + err.stack, 502))
  e.respondWith(ret)
})

/**
 * 判断是否为合法的 GitHub 资源链接
 * @param {string} u
 */
function checkUrl(u) {
  for (let i of [exp1, exp2, exp3, exp4, exp5]) {
    if (u.search(i) === 0) {
      return true
    }
  }
  return false
}

/**
 * 主请求处理逻辑
 * @param {FetchEvent} e
 */
async function fetchHandler(e) {
  const req = e.request
  const urlStr = req.url
  const urlObj = new URL(urlStr)

  // 支持 ?q= 参数跳转(方便调试)
  let path = urlObj.searchParams.get('q')
  if (path) {
    return Response.redirect('https://' + urlObj.host + PREFIX + path, 301)
  }

  // 提取实际要代理的目标 URL
  path = urlObj.href.substr(urlObj.origin.length + PREFIX.length).replace(/^https?:\/+/, 'https://')

  // 根据路径类型分发处理
  if (path.search(exp1) === 0 || path.search(exp5) === 0 || 
      !Config.cnpmjs && (path.search(exp3) === 0 || path.search(exp4) === 0)) {
    return httpHandler(req, path)
  } else if (path.search(exp2) === 0) {
    if (Config.jsdelivr) {
      // 使用 jsDelivr 加速 blob 文件
      const newUrl = path.replace('/blob/', '@').replace(/^(?:https?:\/\/)?github\.com/, 'https://cdn.jsdelivr.net/gh')
      return Response.redirect(newUrl, 302)
    } else {
      // 转为 raw 链接直连
      path = path.replace('/blob/', '/raw/')
      return httpHandler(req, path)
    }
  } else if (path.search(exp3) === 0) {
    // git-info 类请求走 cnpmjs.org 镜像
    const newUrl = path.replace(/^(?:https?:\/\/)?github\.com/, 'https://github.com.cnpmjs.org')
    return Response.redirect(newUrl, 302)
  } else if (path.search(exp4) === 0) {
    // raw.githubusercontent.com 走 jsDelivr
    const newUrl = path.replace(/(?<=com\/.+?\/.+?)\/(.+?\/)/, '@$1')
                      .replace(/^(?:https?:\/\/)?raw\.(?:githubusercontent|github)\.com/, 'https://cdn.jsdelivr.net/gh')
    return Response.redirect(newUrl, 302)
  } else {
    // 其他情况回退到原始 GitHub 页面
    return fetch(ASSET_URL + path)
  }
}

/**
 * 处理 HTTP 请求并转发
 * @param {Request} req
 * @param {string} pathname
 */
function httpHandler(req, pathname) {
  const reqHdrRaw = req.headers

  // 处理 CORS 预检请求
  if (req.method === 'OPTIONS' && reqHdrRaw.has('access-control-request-headers')) {
    return new Response(null, PREFLIGHT_INIT)
  }

  const reqHdrNew = new Headers(reqHdrRaw)
  let urlStr = pathname
  if (urlStr.startsWith('github')) {
    urlStr = 'https://' + urlStr
  }

  const urlObj = newUrl(urlStr)
  if (!urlObj) return makeRes('Invalid URL', 400)

  /** @type {RequestInit} */
  const reqInit = {
    method: req.method,
    headers: reqHdrNew,
    redirect: 'manual',
    body: req.body
  }

  return proxy(urlObj, reqInit)
}

/**
 * 实际执行代理请求
 * @param {URL} urlObj
 * @param {RequestInit} reqInit
 */
async function proxy(urlObj, reqInit) {
  const res = await fetch(urlObj.href, reqInit)
  const resHdrOld = res.headers
  const resHdrNew = new Headers(resHdrOld)
  const status = res.status

  // 处理重定向地址,确保仍走代理
  if (resHdrNew.has('location')) {
    let _location = resHdrNew.get('location')
    if (checkUrl(_location)) {
      resHdrNew.set('location', PREFIX + _location)
    } else {
      reqInit.redirect = 'follow'
      return proxy(newUrl(_location), reqInit)
    }
  }

  // 清除可能干扰前端的头部
  resHdrNew.set('access-control-expose-headers', '*')
  resHdrNew.set('access-control-allow-origin', '*')
  resHdrNew.delete('content-security-policy')
  resHdrNew.delete('content-security-policy-report-only')
  resHdrNew.delete('clear-site-data')

  return new Response(res.body, {
    status,
    headers: resHdrNew,
  })
}

✅ 这段代码已经经过实战验证,能正确处理 GitHub 的 releases、raw、blob、gist 等常见资源类型,并支持灵活切换镜像源。

ad

免费在线工具导航

优网导航旗下整合全网优质免费、免注册的在线工具导航大全


第三步:测试并保存

点击编辑器右上角的 Send(发送)按钮,输入任意 GitHub 链接测试,比如:

https://你的worker域名/https://github.com/fatedier/frp

如果返回 200 OK 并成功加载页面,说明代理工作正常。点击 Save and Deploy 保存部署。


第四步:绑定自定义域名(关键!)

回到 Cloudflare 控制台,在你的 Worker 页面点击 TriggersCustom DomainsAdd Custom Domain

输入你准备好的域名(比如 github.leitingtool.cf),系统会自动为你申请免费的 SSL 证书,几分钟后就能生效。

🌟 小技巧:建议用二级域名(如 gh.yourdomain.com),避免影响主站。


怎么用这个代理?

非常简单!只要在原始 GitHub 链接前加上你的代理地址即可。

例如:

  • 原始仓库克隆地址:
    https://github.com/fatedier/frp.git
  • 代理后的地址:
    https://github.leitingtool.cf/https://github.com/fatedier/frp.git

Git、wget、curl、浏览器下载统统适用!


最后提醒几点

  • 如果你开启了 Config.jsdelivr = 1,blob 文件会走 jsDelivr,速度更快,但首次访问可能有缓存延迟。
  • 免费域名(如 Freenom)存在被回收风险,重要项目建议用付费域名。
  • Cloudflare Workers 免费版有每日请求次数和运行时间限制,个人使用完全够用。
如何使用 Cloudflare 搭建 GitHub 代理(国内可用)

搞定!现在你有了一个属于自己的 GitHub 加速通道,再也不用担心 clone 卡半天了。要是觉得有用,不妨分享给身边同样被 GitHub 折磨的小伙伴~

如需进一步优化(比如配合 Nginx 做路径美化、添加访问日志等),也可以继续扩展。欢迎留言交流!

© 版权声明

相关文章

暂无评论

暂无评论...