常见错误与排查手册
浏览器把许多失败表现为“CORS error”,但根因也可能是 DNS、TLS、Nginx、重定向、CSP 或应用异常。排查时应沿请求链逐层缩小范围。
1. 建立排查顺序
浏览器页面
→ DNS / HTTPS
→ Nginx
→ OPTIONS 预检
→ Express 通用中间件
→ 认证与路由
→ 正式响应
→ 浏览器 CSP/CORS 判断
先在浏览器 Network 中确认:是否发出了 OPTIONS、正式请求是否存在、状态码是什么、响应是否被重定向、Console 报的是 CSP 还是 CORS。
2. curl 三组基础检查
查看响应头和响应体:
curl -i https://api.example.com/api/health
只请求响应头:
curl -I https://www.example.com/
注意:-I 发送 HEAD,某些接口没有实现 HEAD 或行为与 GET 不同。调试 API 时通常优先使用 -i。
检查指定 Origin:
curl -i https://api.example.com/api/health \
-H "Origin: https://www.example.com"
检查预检:
curl -i -X OPTIONS https://api.example.com/api/orders \
-H "Origin: https://www.example.com" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: Content-Type, Authorization"
3. 高频现象对照
| 现象 | 优先检查 |
|---|---|
| Postman 正常、浏览器失败 | CORS、CSP、混合内容、Cookie 策略 |
| OPTIONS 返回 401/403 | CORS 是否位于认证之前,Nginx 是否拦截 OPTIONS |
| OPTIONS 正常但正式请求失败 | 正式响应 CORS Header、认证、业务错误、重定向 |
| API 200,但前端读不到响应 | Allow-Origin、Credentials、浏览器 Console |
| 能获取正文但读不到文件名 | Access-Control-Expose-Headers |
| 页面白屏或脚本被拒绝 | CSP、Content-Type、nosniff |
| 图片跨域加载失败 | 图片 URL、CSP img-src、CORP、CORS |
| 本机 8080 正常,域名异常 | Nginx location、配置未 reload、TLS、Header 覆盖 |
| 仅错误响应显示 CORS 错误 | CORS 中间件顺序、Nginx add_header ... always |
| 修改白名单后仍旧失败 | 浏览器预检缓存、CDN/Nginx 缓存、旧进程未重启 |
4. OPTIONS 被 JWT 中间件拒绝
问题顺序:
app.use(authenticationMiddleware);
app.use(cors(corsOptions));
浏览器的预检通常不携带正式 Authorization,因而认证返回 401。推荐:
app.use(cors(corsOptions));
app.use(authenticationMiddleware);
如果只对某些路由启用 CORS,也必须保证相应 OPTIONS 路径能先到达 CORS 处理逻辑。
5. 错误响应缺失 CORS Header
如果路由或认证在 cors() 之前返回,成功响应可能正常,而 401/500 缺少 Allow-Origin。浏览器随后隐藏真实响应,前端只看到 CORS 报错。
排查时直接使用 curl 查看真实状态码和响应体,并把 CORS 放到可能提前响应的中间件之前。不要通过给每个错误处理器手写一遍 Header 来掩盖顺序问题。
6. Nginx 与 Express 重复设置
执行:
curl -i https://api.example.com/api/health \
-H "Origin: https://www.example.com"
如果看到两个 Access-Control-Allow-Origin,检查:
- Express
cors()。 - Nginx
add_header。 - CDN 或网关规则。
- 多层代理是否重复追加。
确定唯一责任层。动态 Origin 白名单通常更适合由 Express 管理。
7. CORS 正常但 CSP 阻止请求
Console 可能显示:
Refused to connect ... because it violates Content Security Policy directive: connect-src ...
这时请求可能根本没有发出。修改的应是页面响应 CSP 的 connect-src,不是 API 的 Allow-Origin。先确认 CSP Header 来自 Nginx 返回的 HTML,还是某个应用服务器。
8. Cookie 场景专项检查
- 前端是否启用了 credentials。
- 响应是否为精确 Allow-Origin,而不是
*。 - 是否有
Access-Control-Allow-Credentials: true。 - Cookie 是否因
Secure、SameSite、Domain 或 Path 被浏览器拒绝。 - HTTPS 页面是否请求 HTTP API,形成 Mixed Content。
- 写接口是否有 CSRF 防护。
浏览器 Application/Storage 面板可检查 Cookie 是否被存储;Network 面板可检查请求是否真正携带 Cookie。
9. 防止上线才发现问题
- 预发布环境使用与生产相同的 HTTPS 和域名结构。
- 自动验证首页、API、OPTIONS、401、404 和 500 的 Header。
- CSP 先 Report-Only,观察再强制。
- 发布后清楚预检和代理缓存的影响。
- 修改 Nginx 后先
nginx -t,再 reload,并确认实际运行配置。 - 重启应用后确认 PM2 中运行的是新构建产物。
- 保留上一版本配置和快速回滚方案。