Cloudflare Workers代理返回301而非预期200问题求助
问题:Cloudflare Workers代理请求返回301而非预期200
问题详情
需实现代理规则:www.frontend.com/login?queryParams → www.backend.com/login?queryParams,且不暴露后端URL。直接请求后端URL返回200状态码,但通过Cloudflare Workers的fetch调用后,前端收到301永久重定向响应,且Location头暴露了后端地址。
后端URL直接请求的响应头
HTTP/2 200 access-control-allow-origin: * cache-control: max-age=3600 content-type: text/html; charset=utf-8 etag: "4c94e079d45218460e7c0db2d010abfba3a4cff229547fcdeb2d6059333310" last-modified: Wed, 11 Oct 2023 05:24:20 GMT strict-transport-security: max-age=31556926 accept-ranges: bytes date: Tue, 06 Aug 2024 16:07:20 GMT x-served-by: cache-bom4743-BOM x-cache: MISS x-cache-hits: 0 x-timer: S1722960440.785547,VS0,VE269 vary: x-fh-requested-host, accept-encoding alt-svc: h3=":443";ma=86400,h3-29=":443";ma=86400,h3-27=":443";ma=86400 content-length: 25601
Cloudflare Worker返回的301响应头
HTTP/2 301 date: Tue, 06 Aug 2024 16:09:59 GMT content-length: 0 location: https://www.backend.com/login cf-ray: 8af04b640ee63217-BOM cf-cache-status: DYNAMIC retry-after: 0 accept-ranges: bytes alt-svc: h3=":443"; ma=86400 x-cache: HIT x-cache-hits: 0 x-served-by: cache-bom4751-BOM x-timer: S1722960600.703772,VS0,VE0 report-to: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\s=CeseREkv7hOLD9aXW6iL8G9oYh6nSUkSyp5DnEjnw4mTXM3CTN%2F7ppgZ%2BdJYdBjwH5edvop1ztV7SWEKm6bMtBRHq5o0eKetmrvG%2Bc3x7Dtb%2FfIjA%2BJPCg%3D%3D"}],"group":"cf-nel","max_age":604800} nel: {"success_fraction":0,"report_to":"cf-nel","max_age":604800}
当前配置与代码
wrangler.toml配置
name = "fronted-to-backend-proxy" main = "src/index.ts" compatibility_date = "2024-07-29" compatibility_flags = ["nodejs_compat"] routes = [ { pattern = "www.frontend.com/login", zone_name = "company_domain.com"}, { pattern = "www.frontend/refer", zone_name = "company_domain.com"}, ]
Worker代码(index.ts)
export default { async fetch(request, env, ctx): Promise<Response> { // Parse the incoming request URL const url = new URL(request.url); // Define the URL patterns and their corresponding proxy endpoints const proxyMappings: { [key: string]: string } = { "/login": "https://www.backend.com/login", "/refer": "https://www.backend.com/refer" }; // Check if the URL pathname matches any of the specified patterns if (proxyMappings[url.pathname]) { // Modify the URL to point to the new endpoint const proxyUrl = new URL(proxyMappings[url.pathname]); // Append query parameters to the proxy URL url.searchParams.forEach((value, key) => { proxyUrl.searchParams.append(key, value); }); // Create a new request with the modified URL, including headers and session const modifiedRequest = new Request(proxyUrl.toString(), { method: request.method, headers: request.headers, body: request.body, redirect: request.redirect }); // Fetch the modified URL and return the response return fetch(modifiedRequest); } // If no match is found, return a 404 response return new Response("Not Found", { status: 404 }); } } satisfies ExportedHandler<Env>;
环境说明
frontend.com为CNAME记录的子域名,backend.com为A记录的子域名- 二者均在Cloudflare中启用Flexible SSL配置
解决方案建议
1. 拦截并修改重定向响应,隐藏后端URL
后端可能返回了重定向(如HTTP转HTTPS、路径标准化),Worker默认会直接将该重定向返回给前端,导致暴露后端地址。需手动处理重定向:
// 修改fetch部分的代码 const response = await fetch(modifiedRequest, { redirect: 'manual' }); // 如果是重定向状态码,修改Location头为前端域名路径 if (response.status >= 300 && response.status < 400) { const location = response.headers.get('location'); if (location) { // 将后端地址替换为前端对应路径 const redirectUrl = new URL(location); const frontendPath = Object.entries(proxyMappings).find(([_, backendUrl]) => redirectUrl.href.startsWith(backendUrl) )?.[0]; if (frontendPath) { const newLocation = new URL(`${url.origin}${frontendPath}`); newLocation.search = redirectUrl.search; return new Response(response.body, { status: response.status, headers: new Headers(response.headers).set('location', newLocation.href) }); } } } return response;
2. 修改请求的Host头,适配后端配置
Flexible SSL模式下,Worker请求后端时默认携带原请求的Host头(即www.frontend.com),后端可能因Host不匹配返回重定向。需显式设置Host为后端域名:
// 创建modifiedRequest时,修改headers const modifiedHeaders = new Headers(request.headers); modifiedHeaders.set('Host', 'www.backend.com'); const modifiedRequest = new Request(proxyUrl.toString(), { method: request.method, headers: modifiedHeaders, body: request.body, redirect: 'manual' // 配合重定向处理 });
3. 修复路由配置错误
wrangler.toml中的路由www.frontend/refer存在域名缺失,应改为www.frontend.com/refer,否则该路由无法匹配请求。
4. 优化路径匹配逻辑
当前代码仅匹配精确的/login路径,若请求路径为/login/或带额外后缀(如/login/abc)会返回404。建议改为匹配路径前缀:
// 替换原匹配逻辑 const matchedMapping = Object.entries(proxyMappings).find(([path]) => url.pathname.startsWith(path) ); if (matchedMapping) { const [frontendPath, backendUrl] = matchedMapping; const proxyUrl = new URL(backendUrl); // 保留路径后缀(如果需要) proxyUrl.pathname = proxyUrl.pathname + url.pathname.slice(frontendPath.length); // ... 后续处理逻辑 }
内容的提问来源于stack exchange,提问作者Naga Sai
相关产品推荐
相关产品推荐

