NestJS CORS问题排查:Access-Control-Allow-Origin多值异常
我用NestJS开发后端,前端采用React框架。在main.ts里配置了如下CORS规则:
const whitelist = [ process.env.APP_WEBSITE_URL as string, process.env.APP_URL as string, process.env.SUPER_TOKEN_CONNECTION_STRING as string, 'http://localhost:3000/' as string, ]; app.enableCors({ origin: whitelist, allowedHeaders: ['content-type', ...supertokens.getAllCORSHeaders()], credentials: true, methods: ['GET', 'PUT', 'POST', 'OPTIONS', 'PATCH'], });
当前前端部署在app.jetabe.com,后端部署在api.jetabe.com,请求时遇到CORS错误:
Access to fetch at 'https://api.jetabe.com/applicationType' from origin 'https://app.jetabe.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The 'Access-Control-Allow-Origin' header contains multiple values 'https://app.jetabe.com, *', but only one is allowed. Have the server send the header with a valid value, or, if an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
以下是排查Access-Control-Allow-Origin头出现*来源的具体步骤:
检查反向代理/服务器配置
如果后端通过Nginx、Apache等反向代理部署,优先排查代理配置文件,确认是否存在add_header Access-Control-Allow-Origin *;这类规则。代理层添加的CORS头会和NestJS返回的头重复,导致多值冲突。验证NestJS全局中间件与拦截器
排查项目中是否有自定义全局拦截器、中间件,或者第三方模块(除Supertokens外)自动注入了CORS配置。比如有没有手动在响应中添加Access-Control-Allow-Origin头的代码,或者重复注册了cors中间件。核对环境变量与白名单配置
确认APP_WEBSITE_URL、APP_URL等环境变量的值是否为https://app.jetabe.com,注意浏览器发送的origin不带结尾斜杠,白名单里的http://localhost:3000/带斜杠可能导致匹配失败。同时检查环境变量是否正确加载,避免白名单中出现undefined,触发NestJS的CORS逻辑异常。排查Supertokens配置冲突
检查Supertokens的初始化代码,确认是否开启了它自身的CORS配置。如果Supertokens也设置了CORS头,会和NestJS的配置重复,需要禁用其中一方的CORS设置,仅保留NestJS的配置即可。调试CORS匹配逻辑
临时修改CORS的origin为自定义函数,打印请求origin和白名单匹配情况,定位问题:app.enableCors({ origin: (origin, callback) => { console.log('当前请求origin:', origin); console.log('CORS白名单:', whitelist); // 统一格式,去掉URL结尾斜杠 const normalizedWhitelist = whitelist.map(url => url.replace(/\/$/, '')); const normalizedOrigin = origin?.replace(/\/$/, ''); if (normalizedWhitelist.includes(normalizedOrigin) || !origin) { callback(null, origin); } else { callback(new Error('Not allowed by CORS')); } }, allowedHeaders: ['content-type', ...supertokens.getAllCORSHeaders()], credentials: true, methods: ['GET', 'PUT', 'POST', 'OPTIONS', 'PATCH'], });查看后端日志,确认请求origin是否被正确匹配,以及返回的
Access-Control-Allow-Origin头是否符合预期。
内容的提问来源于stack exchange,提问作者codeForCoffee

