You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 20:57:32