基于Cloudflare Workers构建轻量级反向代理:无服务器边缘计算实践
1. 项目概述一个基于Cloudflare Workers的轻量级反向代理工具最近在折腾个人项目经常遇到需要将一些API或者静态资源部署到全球但又不想搞个服务器那么麻烦。VPS要钱还要维护对于个人开发者或者小团队来说成本有点高。这时候Cloudflare Workers就进入了我的视野。它是一个无服务器函数平台在全球有几百个节点能让你写的代码在离用户最近的地方运行延迟低还免费。mduongvandinh/openclaw-cloudflare这个项目就是一个专门为Cloudflare Workers设计的反向代理工具你可以把它理解为一个轻量级的“爪子”帮你把请求抓到别的地方去处理再返回结果。简单来说它就是一个跑在Cloudflare边缘网络上的、用JavaScript写的反向代理。你不需要服务器只需要一个Cloudflare账户就能把对某个域名的请求转发到你指定的后端服务比如你的家庭NAS、某个内网API、或者另一个不支持HTTPS的旧服务。这对于需要暴露内网服务、做API聚合、或者给某些服务套上Cloudflare强大的安全与CDN能力来说非常有用。我最初用它是为了安全地访问家里树莓派上跑的服务不用在路由器上做复杂的端口映射也不用担心公网IP变化的问题。2. 核心设计思路与架构拆解2.1 为什么选择Cloudflare Workers首先得明白我们面对的核心痛点低成本、全球化、易部署、高可用。传统的反向代理方案比如Nginx需要一台有公网IP的服务器。这台服务器就成了单点如果它宕机或者所在区域网络不佳服务就挂了。而且服务器的带宽和流量都是要钱的尤其是面对突发流量时。Cloudflare Workers完美地解决了这些问题无服务器与全球边缘网络代码部署在Cloudflare的全球边缘节点上用户访问时请求由离他最近的节点处理。这意味着极低的网络延迟和天生的高可用性一个节点挂了流量会自动路由到其他节点。免费额度慷慨免费计划每天提供10万次请求对于个人项目、博客、或者中小型API来说完全够用。这几乎实现了零成本运维。开发部署极其简单不需要配置服务器环境不需要管理运行时。直接在Web编辑器里写JavaScript/TypeScript代码或者通过Wrangler CLI工具一键部署整个流程几分钟就能搞定。内置安全与性能特性自动获得HTTPS、DDoS防护、缓存等Cloudflare全家桶功能省去了大量配置工作。openclaw-cloudflare就是基于这些优势将反向代理的核心逻辑封装成一个简洁的Worker脚本。它的设计目标不是替代Nginx这种功能全面的网关而是在Cloudflare Workers这个特定环境下提供一个开箱即用、配置灵活的反向代理解决方案。2.2 项目架构与核心工作流这个项目的架构非常直接核心就是一个JavaScript文件通常是index.js或worker.js它导出一个处理fetch事件的函数。Cloudflare Workers平台会监听你绑定的域名或路由当有请求到达时就执行这个函数。它的核心工作流可以分解为以下几步请求拦截Worker接收到用户发起的HTTP/HTTPS请求。规则匹配与目标URL重写根据预设的规则比如路径前缀、域名解析出用户想要访问的实际后端服务地址。这是反向代理的核心逻辑openclaw-cloudflare通常通过配置一个“目标主机”映射表来实现。请求转发Worker构造一个新的Request对象将原始请求的头部、方法、体等转发到上一步确定的后端URL。这里可以添加或修改头部比如添加认证信息、修改Host头等。响应处理收到后端服务的响应后Worker可以对响应进行一些处理比如修改头部、状态码或者简单的响应体转换然后再返回给最终用户。错误处理如果后端服务不可达、超时或返回错误Worker需要返回一个友好的错误页面或状态码而不是将内部错误信息直接暴露给用户。整个过程中用户感知的始终是你的Cloudflare Workers域名比如proxy.yourdomain.com完全不知道背后真正的服务在哪里。这既保护了后端服务的真实地址也让你能灵活地迁移后端服务而不影响用户。3. 核心配置与功能详解3.1 环境变量与配置管理一个健壮的反向代理需要灵活的配置。openclaw-cloudflare通常利用Cloudflare Workers的环境变量Environment Variables或KV命名空间来存储配置避免将敏感信息如后端地址、API密钥硬编码在代码中。常见的配置项包括UPSTREAM_URL或TARGET_HOST这是最核心的配置指定默认的后端服务基础URL。例如https://api.internal.com。路径重写规则你可能只想代理特定路径。例如将所有访问/api/*的请求转发到后端而/static/*的请求则直接由Cloudflare的CDN处理。这需要在代码逻辑中实现路径匹配和替换。请求头修改转发时经常需要修改或添加头部。比如将Host头设置为后端服务认可的值或者添加一个X-Forwarded-For头告知后端真实用户IP。超时与重试策略设置转发请求的超时时间如30秒以及失败时是否重试、重试几次。这能防止一个慢后端拖垮整个Worker。在代码中你可以通过env对象来访问这些环境变量。一个典型的配置读取逻辑如下export default { async fetch(request, env, ctx) { // 从环境变量读取上游地址 const upstreamUrl env.UPSTREAM_URL; // 如果没有配置可以提供一个默认值或直接返回错误 if (!upstreamUrl) { return new Response(上游服务未配置, { status: 502 }); } // ... 后续代理逻辑 } }注意环境变量在Worker部署时注入修改后需要重新部署才能生效。对于需要频繁动态变更的配置可以考虑使用Cloudflare KV来存储。3.2 请求转发与响应处理的实现细节这是代码的核心部分。我们来看一个简化但功能完整的实现片段export default { async fetch(request, env, ctx) { // 1. 定义上游基础URL const baseUpstream env.UPSTREAM_URL || ‘https://default-backend.example.com‘; // 2. 解析原始请求的URL const url new URL(request.url); const path url.pathname; const search url.search; // 3. 构建新的上游请求URL (这里示例为简单拼接实际可根据规则复杂匹配) const upstreamUrl ${baseUpstream}${path}${search}; // 4. 准备新的请求头 const newHeaders new Headers(request.headers); // 重要修改Host头许多后端服务依赖此头进行虚拟主机路由 newHeaders.set(‘Host‘, new URL(baseUpstream).host); // 添加X-Forwarded-For头传递用户真实IPCloudflare会将真实IP放在CF-Connecting-IP头中 const userIP request.headers.get(‘CF-Connecting-IP‘); if (userIP) { newHeaders.set(‘X-Forwarded-For‘, userIP); } // 5. 构造新的请求对象 const newRequest new Request(upstreamUrl, { method: request.method, headers: newHeaders, body: request.body, redirect: ‘manual‘, // 不自动跟随重定向由Worker处理 }); // 6. 发起转发请求并设置超时 const timeoutMs 10000; // 10秒超时 const timeoutPromise new Promise((_, reject) setTimeout(() reject(new Error(‘上游服务响应超时‘)), timeoutMs) ); try { // 使用Promise.race实现超时控制 const response await Promise.race([fetch(newRequest, { cf: { resolveOverride: new URL(baseUpstream).hostname } }), timeoutPromise]); // 7. 可选处理响应例如修改响应头 const newResponse new Response(response.body, response); // 添加CORS头如果后端没有设置的话 newResponse.headers.set(‘Access-Control-Allow-Origin‘, ‘*‘); // 移除一些可能暴露上游信息的头部 newResponse.headers.delete(‘x-powered-by‘); return newResponse; } catch (error) { // 8. 错误处理 console.error(代理请求失败: ${error.message}); return new Response(‘网关错误请稍后重试‘, { status: 502, headers: { ‘Content-Type‘: ‘text/plain;charsetutf-8‘ }, }); } }, };关键点解析cf: { resolveOverride }这是Cloudflare Workers特有的fetch选项。它强制Worker的DNS解析指向指定的主机名这在你的上游服务域名也使用了Cloudflare且与Worker在同一账户时非常有用可以避免循环解析问题。超时控制边缘函数的执行时间有限制免费计划10ms CPU时间但网络I/O时间另计。为转发请求设置超时是必须的防止坏的上游服务耗尽Worker资源。头部处理正确处理Host和X-Forwarded-For头是反向代理工作的关键。许多Web框架如Spring Boot, Express依赖这些头来做安全校验、日志记录或路由。错误隔离用try...catch包裹转发逻辑确保任何错误网络错误、超时、上游5xx错误都能被捕获并返回一个统一的5xx错误页面而不是将内部堆栈信息泄露出去。3.3 高级功能路径重写、多目标与负载均衡基础的代理只能转发到单一后端。openclaw-cloudflare这类项目的高级形态往往会支持更复杂的路由规则。路径前缀剥离/重写用户访问https://proxy.example.com/api/v1/users但你希望后端收到的是/v1/users。这就需要从请求路径中剥离/api前缀。const path url.pathname; let upstreamPath path; if (path.startsWith(‘/api/‘)) { upstreamPath path.replace(‘^/api‘, ‘‘); // 剥离 /api 前缀 } const upstreamUrl ${baseUpstream}${upstreamPath}${search};多目标路由基于路径或域名你可以配置一个映射表将不同的路径指向不同的后端服务。const routeMap { ‘/service1/‘: ‘https://backend1.example.com‘, ‘/service2/‘: ‘https://backend2.example.net‘, ‘/blog/‘: ‘https://my-cms.workers.dev‘, }; // 遍历映射表找到匹配的前缀 for (const [prefix, target] of Object.entries(routeMap)) { if (path.startsWith(prefix)) { upstreamBase target; // 可以在这里进行路径重写 break; } }简易负载均衡虽然Workers本身不是专业的负载均衡器但可以实现简单的轮询策略将请求分发到多个后端实例提高可用性。const backends [ ‘https://backend-a.example.com‘, ‘https://backend-b.example.com‘, ‘https://backend-c.example.com‘, ]; let currentIndex 0; // 注意Worker是无状态的这个索引在多次请求间无法保持需要外部存储。 // 更实用的方法使用随机选择或根据请求的某些特征如用户ID哈希选择后端 const selectedBackend backends[Math.floor(Math.random() * backends.length)];重要提醒Worker是无状态且瞬时执行的传统的内存轮询索引无效。要实现有状态的负载均衡需要借助外部存储如Durable Objects或KV但这会引入复杂度。对于简单场景随机选择或一致性哈希基于请求URL或IP是更可行的方案。4. 完整部署与配置实操指南4.1 前期准备与环境搭建在开始之前你需要准备好以下几样东西一个Cloudflare账户如果你没有去官网免费注册一个。一个域名可选但推荐你可以使用Cloudflare分配的*.workers.dev子域名但为了专业性和灵活性建议使用自己的域名并将其DNS托管到Cloudflare。Node.js与npm环境用于安装和运行Wrangler CLI工具。Wrangler CLICloudflare官方命令行工具用于管理Workers。通过npm install -g wrangler安装。首先登录Wranglerwrangler login这会打开浏览器授权Wrangler访问你的Cloudflare账户。4.2 项目初始化与代码获取你可以从mduongvandinh/openclaw-cloudflare的GitHub仓库获取代码。假设我们创建一个新项目# 创建一个新目录 mkdir my-openclaw-proxy cd my-openclaw-proxy # 初始化一个Wrangler项目 wrangler init # 在初始化过程中选择“JavaScript”或“TypeScript”模板。初始化后你会得到wrangler.toml配置文件和src/index.js入口文件等。接下来将openclaw-cloudflare的核心代理逻辑代码复制到src/index.js中替换掉默认内容。或者你也可以直接克隆原仓库在其基础上修改。4.3 配置 wrangler.toml 与环境变量wrangler.toml是项目的核心配置文件。一个基本的配置如下name my-openclaw-proxy # Worker的名称 main src/index.js # 入口文件 compatibility_date 2024-03-20 # 兼容性日期很重要 # 配置你的自定义域名如果你有自己的域名 # routes [ # { pattern proxy.yourdomain.com/*, zone_name yourdomain.com } # ] # 使用 workers.dev 域名 workers_dev true # 定义环境变量 [vars] UPSTREAM_URL https://your-real-backend.com # 你的后端服务地址 # 可以定义多个变量如 API_KEY、TIMEOUT等 # 如果你需要更动态的配置可以绑定一个KV命名空间 # [[kv_namespaces]] # binding PROXY_CONFIG # id xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx关键配置项说明compatibility_date指定Worker运行时的兼容性日期。Cloudflare会不断更新运行时这个日期确保你的代码行为在特定版本下保持一致。建议设置为最近的日期。routes将Worker绑定到你的自定义域名的特定路由。例如将所有发往proxy.yourdomain.com的请求都交给这个Worker处理。[vars]这里定义的环境变量在Worker代码中可以通过env.变量名访问。切勿将密码、密钥等敏感信息直接写在这里对于敏感信息应使用wrangler secret put命令设置。设置秘密环境变量wrangler secret put MY_API_KEY # 随后命令行会提示你输入值输入的值会被加密存储。4.4 本地开发、测试与部署本地开发Wrangler提供了强大的本地开发服务器可以模拟Cloudflare边缘环境。wrangler dev启动后默认在http://localhost:8787提供服务。你可以在浏览器访问或使用curl、Postman等工具测试你的代理逻辑。wrangler dev支持热重载修改代码后会自动刷新。测试在本地你可以通过修改环境变量来测试不同的上游地址。创建一个.dev.vars文件不会被提交到Git其内容类似于UPSTREAM_URLhttp://localhost:3000 # 指向你本地运行的后端服务在运行wrangler dev时它会自动加载这个文件中的变量。部署到生产环境测试无误后部署非常简单。wrangler deploy这条命令会将你的Worker代码打包并发布到Cloudflare全球网络。部署成功后你会得到一个*.workers.dev的URL或者如果你配置了自定义路由就可以通过你自己的域名访问了。4.5 域名绑定与SSL证书如果你使用自己的域名例如proxy.yourdomain.com确保你的域名DNS由Cloudflare托管即NS记录指向Cloudflare。在Cloudflare仪表板的Workers Pages部分找到你部署的Worker进入Triggers标签页。点击Add Route输入你的路由例如proxy.yourdomain.com/*。保存后Cloudflare会自动为这个路由配置SSL证书HTTPS整个过程完全免费且自动化。至此一个功能完整的、基于Cloudflare Workers的反向代理服务就搭建完成了。用户访问https://proxy.yourdomain.com/any/path请求会被你的Worker拦截转发到UPSTREAM_URL对应的后端并将响应返回给用户。5. 性能调优、安全加固与监控5.1 性能优化策略虽然Workers本身性能很高但代理逻辑写不好也会成为瓶颈。减少不必要的计算和转发静态资源直出对于图片、CSS、JS等静态资源如果它们已经托管在Cloudflare CDN或另一个快的源站可以考虑让请求直接通过而不经过Worker代理逻辑。可以在代码开头进行判断if (url.pathname.endsWith(‘.jpg‘) || url.pathname.endsWith(‘.css‘) || url.pathname.endsWith(‘.js‘)) { // 直接转发到某个CDN或者直接返回fetch(request) return fetch(request); }缓存响应对于不经常变化的API响应可以利用Worker Cache API或Cloudflare CDN缓存显著减少回源请求降低延迟和上游压力。const cache caches.default; let response await cache.match(request); if (!response) { response await fetch(upstreamRequest); // 设置缓存例如缓存1分钟 ctx.waitUntil(cache.put(request, response.clone())); } return response;优化网络请求使用持久连接HTTP/1.1 Keep-Alive, HTTP/2Cloudflare的fetchAPI默认会优化连接复用我们只需确保不要为每个请求创建不必要的连接开销。压缩传输确保后端返回的响应支持gzip或br压缩Worker会保持这些压缩头减少传输数据量。控制超时与重试为fetch设置合理的超时如5-10秒避免等待缓慢的上游。对于非幂等的请求如POST要谨慎使用重试以免重复提交。5.2 安全加固措施将服务暴露在公网安全是第一要务。输入验证与路径遍历防护严格检查请求路径防止攻击者使用../等字符进行路径遍历访问到上游服务器上的敏感文件。const pathname new URL(request.url).pathname; if (pathname.includes(‘..‘) || pathname.includes(‘//‘)) { return new Response(‘Invalid path‘, { status: 400 }); }限制请求方法与大小只允许必要的HTTP方法如GET, POST并限制请求体大小防止DoS攻击。const ALLOWED_METHODS [‘GET‘, ‘POST‘, ‘PUT‘, ‘DELETE‘]; if (!ALLOWED_METHODS.includes(request.method)) { return new Response(‘Method Not Allowed‘, { status: 405 }); } // 检查Content-Length头 const contentLength request.headers.get(‘content-length‘); if (contentLength parseInt(contentLength) 10 * 1024 * 1024) { // 10MB return new Response(‘Payload Too Large‘, { status: 413 }); }敏感信息过滤在将上游响应返回给用户前仔细检查响应头移除可能泄露后端信息的头部如Server、X-Powered-By、X-AspNet-Version等。身份验证与授权可以在Worker层实现统一的API网关认证。例如检查请求头中的Authorization令牌是否有效。const authToken request.headers.get(‘Authorization‘); if (!isValidToken(authToken)) { return new Response(‘Unauthorized‘, { status: 401 }); } // isValidToken 函数可以查询KV或调用外部认证服务利用Cloudflare防火墙规则在Cloudflare仪表板中为你的Worker路由设置防火墙规则WAF可以轻松屏蔽特定IP、国家/地区或防御SQL注入、XSS等常见Web攻击。5.3 日志、监控与告警无服务器架构下可观测性尤为重要。日志记录使用console.log()、console.error()输出的日志可以在Cloudflare仪表板的Workers Pages- 选择你的Worker -Logs中查看。建议记录关键信息如请求ID、上游URL、响应状态码、处理时间等。const startTime Date.now(); // ... 处理逻辑 ... const duration Date.now() - startTime; console.log(Proxied ${request.url} to ${upstreamUrl} - Status: ${response.status} - Duration: ${duration}ms);监控指标Cloudflare提供Worker的请求次数、错误次数、CPU时间消耗等基本指标。你可以在仪表板的Analytics Logs-Workers中查看。关注错误率5xx和超时比例。设置告警在Cloudflare仪表板中可以基于这些指标设置告警。例如当5分钟内的错误率超过1%时发送邮件或Slack通知让你能及时发现问题。分布式追踪对于复杂场景可以考虑集成像OpenTelemetry这样的分布式追踪将Worker的请求作为整个调用链的一环便于排查跨服务的性能问题。6. 常见问题与故障排查实录在实际使用中你肯定会遇到各种各样的问题。下面是我踩过的一些坑和解决方案。6.1 问题排查清单问题现象可能原因排查步骤与解决方案访问返回502 Bad Gateway1. 上游服务地址 (UPSTREAM_URL) 配置错误或服务未启动。2. Worker中网络请求超时。3. 上游服务SSL证书问题自签名或过期。4. Worker代码运行时错误未捕获的异常。1.检查配置确认env.UPSTREAM_URL是否正确在本地用curl或浏览器测试该地址是否可达。2.检查超时设置增加fetch的超时时间或在try...catch中打印具体错误信息。3.检查SSL如果上游是自签名证书在开发环境可以暂时在fetch选项中设置{ cf: { tlsVerify: false } }生产环境强烈不推荐。生产环境应使用有效证书。4.查看日志在Cloudflare Worker日志中查看是否有未捕获的异常堆栈。返回403 Forbidden或上游服务拒绝请求1.Host头不正确上游服务基于虚拟主机配置拒绝了请求。2. 上游服务有IP白名单而Cloudflare Worker的出口IP不在名单内。1.检查请求头确保在转发请求时正确设置了Host: upstream-host头。2.获取Worker出口IPCloudflare Worker的出口IP是动态的。你可以创建一个简单的Worker返回request.headers.get(‘CF-Connecting-IP‘)和request.cf.asOrganization等信息找出当前区域使用的IP段然后将其添加到上游服务的白名单中。CORS跨域错误上游服务返回的响应缺少必要的CORS头部如Access-Control-Allow-Origin。在Worker中添加CORS头在返回给客户端的响应中手动添加CORS头部。注意对于预检请求OPTIONS方法需要单独处理并直接返回包含CORS头的成功响应。响应缓慢延迟高1. 上游服务本身响应慢。2. Worker到上游服务的网络链路不佳。3. Worker代码中存在性能瓶颈如复杂的同步计算。1.测量各阶段耗时在Worker代码中记录接收请求、转发请求、收到响应的时间点计算网络耗时和自身处理耗时。2.使用cf.resolveOverride确保正确使用此选项避免DNS解析到错误IP。3.优化代码避免在Worker中执行耗时的同步操作如大型JSON解析/序列化使用流式处理响应体。部署失败 (wrangler deploy报错)1.wrangler.toml配置语法错误。2. 账户权限不足。3. 同名Worker已存在或被占用。1.检查TOML文件使用在线TOML校验工具检查语法。2.检查登录状态运行wrangler whoami确认当前登录的账户有权限部署到目标账户。3.修改Worker名称在wrangler.toml中修改name字段为一个唯一的名字。6.2 实战避坑经验Host头是万恶之源90%的代理问题都和Host头有关。很多后端框架Nginx虚拟主机、Spring Boot的server.servlet.context-path等都依赖Host头来做路由。务必在转发前将Host头设置为上游服务域名的主机部分不含端口。同时保留原始Host头的信息可以通过X-Forwarded-Host传递。小心循环代理如果你的上游服务也使用了Cloudflare并且和Worker在同一个域名下可能会形成循环代理。例如WorkerA代理到service.yourdomain.com而service.yourdomain.com的DNS又指向Cloudflare并且也触发了一个WorkerB或回源到A。使用cf: { resolveOverride: ‘your-real-origin-ip‘ }可以强制DNS解析到源站IP打破循环。处理流式响应和大文件默认情况下await response.text()或await response.json()会将整个响应体读入内存。对于大文件或流式响应如视频这会耗尽Worker的内存限制免费计划128MB。应该使用响应体的流式接口const upstreamResponse await fetch(newRequest); const { readable, writable } new TransformStream(); upstreamResponse.body.pipeTo(writable); return new Response(readable, upstreamResponse);这样数据是流式地从上游传输到客户端Worker只充当管道不占用大量内存。环境变量与秘密管理永远不要将密钥、密码等硬编码在代码或wrangler.toml的[vars]中。务必使用wrangler secret put命令。对于需要区分开发和生产的环境变量可以利用wrangler.toml中的[env.dev]、[env.production]节结合wrangler deploy --env来管理。免费额度的瓶颈免费计划每天10万次请求对于大多数个人项目足够。但要注意CPU时间限制。如果你的代理逻辑非常复杂或者上游响应极慢导致请求挂起时间过长都可能消耗大量CPU时间。监控你的Worker的“Duration”指标优化代码设置合理的超时是避免超额的关键。通过这个项目你将得到一个极其灵活、强大且成本极低的边缘代理工具。它不仅仅是简单的请求转发更是一个可以植入业务逻辑认证、限流、修改响应的全球化接入点。无论是用于个人项目、初创公司原型还是作为现有架构的补充Cloudflare Workers 反向代理的模式都值得你深入探索和实践。