Node.js API配置cors中间件后仍触发CORS跨域错误的排查求助
跨域异常根因定位
- 梳理现有配置中的明确硬伤,可完全对应你碰到的所有异常现象:
- 配置值不匹配:api.example.com的Nginx块里,
Access-Control-Allow-Origin写的是https://api.example.com,但实际请求来源是https://example.com,允许的源和实际请求源不一致,浏览器自然拦截。 - 响应头传递失效:Nginx默认的
add_header规则,不会自动给上游返回的4xx/5xx状态响应附加头,当接口返回404时,所有CORS头都会丢失,刚好对应“CORS报错伴随404状态”的现象。 - 域名跳转规则缺失:现有Nginx配置里,完全没有给api.example.com配置80端口转443的规则,当请求走http协议访问api域名时,Nginx找不到匹配的服务块,直接返回无任何CORS头的404。
- 双层CORS配置冲突:同时在Node层开启cors中间件、在Nginx层又加了一遍CORS头,两层头如果值不一致、或者出现重复的
Access-Control-Allow-Origin字段,会直接让浏览器判定CORS校验失败。
- 配置值不匹配:api.example.com的Nginx块里,
- 对应“pm2运行时404、yarn start直跑就正常”的现象:本质是pm2常驻的Node进程没有加载最新代码,要么端口和Nginx代理的5000端口不匹配,要么进程还是旧版本,根本没注册
/questions/getTags路由,所以接口直接返回404;因为进程代码过旧,连cors中间件的配置都没生效,自然没有允许源的响应头。
分步修复操作
二选一处理CORS,禁止同时在两层加配置
方案1:在Node层统一处理CORS
- 把Nginx两个server块里所有和CORS相关的
add_header配置全部删除,避免冲突 - 替换Node里裸调用的
cors(),改成明确配置的版本,同时给全局404响应也加上CORS处理:// 替换原来的 app.use(cors()); app.use(cors({ origin: ['https://example.com', 'https://www.example.com'], credentials: true, optionsSuccessStatus: 200 })); // 兜底所有未匹配路由的404响应,确保也带CORS头 app.use((req, res) => { res.status(404).send('Not Found'); }); - 给api.example.com补上80端口转443的Nginx规则:
server { server_name api.example.com; listen 80; return 301 https://$host$request_uri; }
方案2:在Nginx层统一处理CORS
- 把Node代码里的cors中间件完全注释/删除,避免双层头冲突
- 修改api域名的Nginx配置,所有CORS头加
always参数(保证所有状态码的响应都带头),单独处理OPTIONS预检请求,补上代理传递Host头的规则:server { server_name api.example.com; listen 443 ssl; # Certbot的ssl证书配置保持原样不动 add_header 'Access-Control-Allow-Origin' 'https://example.com' always; add_header 'Access-Control-Allow-Credentials' 'true' always; add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range' always; add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH' always; # 单独拦截OPTIONS预检请求,直接返回204,不用透传给Node if ($request_method = OPTIONS) { add_header 'Access-Control-Allow-Origin' 'https://example.com' always; add_header 'Access-Control-Allow-Credentials' 'true' always; add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range' always; add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH' always; add_header 'Content-Length' 0; return 204; } location / { proxy_pass http://127.0.0.1:5000; proxy_http_version 1.1; # 补上Host头传递,避免后端识别不到正确的请求来源 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; } } # 补上api域名80端口跳转规则 server { server_name api.example.com; listen 80; return 301 https://$host$request_uri; }
排查pm2进程状态
- 执行
pm2 list确认常驻Node进程的监听端口和Nginx代理的5000端口完全匹配 - 执行
pm2 logs查看进程运行日志,确认/questions/getTags路由是否正常注册,有没有启动报错 - 修改完Node代码后,必须执行
pm2 restart 进程名让最新代码生效,不要只改文件不重启进程
快速验证技巧
- 在服务器本地执行命令,直接看返回头的CORS配置是否正确,无需打开浏览器调试:
只要返回头里能看到对应curl -I -X GET https://api.example.com/questions/getTags -H "Origin: https://example.com"Access-Control-Allow-Origin字段,后端配置就是生效的,浏览器的报错一般是之前的缓存或者进程没重启导致的。 - 看Network面板里404响应的Server响应头:如果字段是
Nginx,说明是Nginx路由/配置问题;如果是Node返回的内容,说明是Node进程没有加载到最新的路由代码。
内容的提问来源于stack exchange,提问作者Myka
相关产品推荐
相关产品推荐

