1. 理解CORS机制与Nginx的角色
跨域资源共享(CORS)是现代Web开发中无法回避的核心问题。当你的前端应用尝试从不同域名、端口或协议的后端获取资源时,浏览器会像严格的安检员一样拦截这些请求——这就是著名的"同源策略"在起作用。而Nginx作为反向代理的瑞士军刀,正是解决这个问题的绝佳工具。
为什么需要特别关注预检请求(Preflight)?当你的AJAX请求满足以下任一条件时:
- 使用了PUT/DELETE等非简单方法
- 携带了自定义头部(如X-Requested-With)
- Content-Type不是application/x-www-form-urlencoded、multipart/form-data或text/plain
浏览器会先发送OPTIONS请求探路。我曾见过团队花费两天排查的"神秘404",最终发现是Nginx未正确处理OPTIONS请求。这引出了我们的核心命题:Nginx必须同时处理好预检请求和实际请求两条路径。
2. Nginx基础CORS配置解剖
下面这个配置模板是我在多个生产环境验证过的可靠方案。注意add_header指令的微妙之处——它只在当前区块有效,这就是为什么我们需要在OPTIONS和常规请求中重复声明:
nginx复制server {
listen 80;
server_name api.yourdomain.com;
location / {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' 'https://yourfrontend.com';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
add_header 'Access-Control-Allow-Headers'
'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
add_header 'Access-Control-Allow-Origin' 'https://yourfrontend.com';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
add_header 'Access-Control-Allow-Headers'
'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
proxy_pass http://backend;
# 其他proxy配置...
}
}
关键参数解析:
Access-Control-Max-Age:建议设置为1728000秒(20天),避免频繁预检Access-Control-Expose-Headers:让前端能读取的特殊响应头- 务必指定具体域名而非通配符
*,特别是需要携带Cookie时
3. 生产环境进阶配置技巧
3.1 动态允许来源域名
硬编码允许的域名不够灵活。试试这个方案:
nginx复制map $http_origin $cors_origin {
default "";
"~^https://(app1|app2)\.yourdomain\.com$" $http_origin;
}
server {
# ...
add_header 'Access-Control-Allow-Origin' $cors_origin;
add_header 'Vary' 'Origin'; # 重要!避免缓存错误响应
}
3.2 处理WebSocket跨域
WebSocket连接也需要特殊处理:
nginx复制location /socket.io/ {
proxy_pass http://socket_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# CORS头
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' $cors_origin;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' '*';
add_header 'Access-Control-Max-Age' 1728000;
return 204;
}
}
3.3 安全加固建议
- 永远限制
Access-Control-Allow-Methods到最小必要集合 - 使用正则严格校验允许的域名
- 敏感接口建议关闭CORS,改用同域API网关
4. 常见问题排雷指南
4.1 为什么我的CORS配置不生效?
检查清单:
- 确认Nginx配置已重载(
nginx -s reload) - 使用curl测试OPTIONS请求:
curl -X OPTIONS -H "Origin: http://test.com" -I http://api.com - 检查响应头是否包含
Access-Control-Allow-Origin - 查看浏览器控制台错误信息
4.2 遇到"Multiple CORS header"错误
这是典型的重复配置导致的问题。可能原因:
- 在http/server/location多个层级重复定义
- 上游服务(如Node.js)也设置了CORS头
解决方案:统一在Nginx层管理CORS头
4.3 带Cookie的请求失败
需要满足三个条件:
- 前端设置
withCredentials: true - Nginx配置指定具体域名(不能用
*) - 添加头:
add_header 'Access-Control-Allow-Credentials' 'true'
5. 性能优化与调试技巧
5.1 减少预检请求开销
- 合理设置
Access-Control-Max-Age - 合并API端点,减少跨域请求次数
- 对静态资源使用CDN同域部署
5.2 使用Nginx日志调试
在配置中添加调试日志:
nginx复制log_format cors_debug '$remote_addr - $http_origin - $request_method - '
'$http_access_control_request_method - '
'$http_access_control_request_headers';
access_log /var/log/nginx/cors.log cors_debug;
5.3 监控CORS请求
通过Prometheus监控异常CORS请求:
nginx复制location /metrics {
stub_status on;
access_log off;
allow 127.0.0.1;
deny all;
}
配合Grafana设置告警规则,监控OPTIONS请求的异常增长。
