作为Web开发中绕不开的经典问题,跨域请求限制源于浏览器的同源策略(Same-Origin Policy)。这个安全机制要求脚本发起的请求必须与当前页面同源(协议+域名+端口三者相同),否则请求会被浏览器拦截。在实际业务场景中,前后端分离架构、微服务调用、第三方API集成等场景都不可避免地需要跨域访问。
Nginx作为反向代理服务器,可以通过配置HTTP响应头来优雅地解决跨域问题。相比在应用代码中处理CORS(如Spring的@CrossOrigin注解),Nginx层配置具有三大优势:
我曾为某电商平台配置Nginx跨域时,通过合理的头部设置将OPTIONS预检请求的响应时间从200ms降至50ms,显著提升了前端性能体验。
Nginx的配置文件采用模块化设计,通常包含:
建议的配置管理实践:
bash复制# 最佳实践:每个服务独立配置文件
/etc/nginx/conf.d/
├── api-gateway.conf
├── static-resources.conf
└── admin-console.conf
# 检查配置语法(重要!)
sudo nginx -t
nginx复制# 单域名白名单
add_header 'Access-Control-Allow-Origin' 'https://www.example.com';
# 多域名动态匹配(需配合map指令)
map $http_origin $cors_origin {
default "";
"~^https://(www\.)?example\.com$" $http_origin;
"~^https://api\.partner\.com$" $http_origin;
}
server {
add_header 'Access-Control-Allow-Origin' $cors_origin;
}
关键细节:当需要传递Cookie时,必须指定具体域名且不能使用通配符*
nginx复制# 标准RESTful API配置
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, PATCH, DELETE, OPTIONS';
# 特殊场景:WebDAV扩展方法
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PROPFIND, MKCOL';
nginx复制# 允许前端访问的自定义响应头
add_header 'Access-Control-Expose-Headers' 'X-Total-Count, X-Request-ID';
nginx复制server {
listen 443 ssl;
server_name api.example.com;
# SSL配置省略...
location /v1/ {
# 核心CORS配置
add_header 'Access-Control-Allow-Origin' $cors_origin always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
add_header 'Access-Control-Max-Age' 1728000 always; # 20天缓存
# 预检请求处理
if ($request_method = 'OPTIONS') {
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
# 反向代理配置
proxy_pass http://backend-service:8080;
proxy_set_header Host $host;
}
}
nginx复制# 全局跨域映射配置
map $http_origin $cors_origin {
default "";
"~^https://(www\.)?example\.com(:[0-9]+)?$" $http_origin;
"~^https://([a-z0-9-]+\.)?example\.net$" $http_origin;
"~^http://localhost(:[0-9]+)?$" $http_origin;
}
# 安全增强头
add_header X-Frame-Options "DENY";
add_header X-Content-Type-Options "nosniff";
add_header Referrer-Policy "strict-origin-when-cross-origin";
常见问题现象及解决方案:
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 预检请求失败 | 缺少OPTIONS方法支持 | 确保Nginx配置处理OPTIONS请求 |
| 携带Cookie失败 | Allow-Origin包含* | 改为具体域名并设置Allow-Credentials |
| 自定义头未生效 | 未配置Expose-Headers | 添加对应头到暴露列表 |
| 跨域策略不生效 | 配置未加载 | 检查nginx -t并reload服务 |
调试命令示例:
bash复制# 查看Nginx加载的完整配置
sudo nginx -T
# 实时监控错误日志
tail -f /var/log/nginx/error.log
# 测试CORS头部响应
curl -I -X OPTIONS https://api.example.com/v1/users \
-H "Origin: https://www.example.com" \
-H "Access-Control-Request-Method: POST"
nginx复制# 将默认的5秒缓存提升到2小时
add_header 'Access-Control-Max-Age' 7200;
nginx复制# 只记录异常的CORS请求
map $http_origin $loggable {
default 0;
"~*example\.com" 0;
"~*localhost" 0;
1;
}
access_log /var/log/nginx/cors.log combined if=$loggable;
nginx复制location ~* \.(js|css|png|jpg|gif)$ {
add_header 'Access-Control-Allow-Origin' '*';
expires 30d;
access_log off;
}
nginx复制# 根据环境变量动态设置
map $env_DEPLOY_ENV $allow_origin {
"prod" "https://www.example.com";
"staging" "https://staging.example.com";
default "http://localhost:3000";
}
server {
add_header 'Access-Control-Allow-Origin' $allow_origin;
}
nginx复制location /ws/ {
proxy_pass http://websocket-backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# WebSocket特殊CORS处理
if ($http_origin ~* (example\.com|localhost)) {
add_header 'Access-Control-Allow-Origin' $http_origin;
add_header 'Access-Control-Allow-Credentials' 'true';
}
}
nginx复制location /api/ {
proxy_pass http://upstream;
# 确保代理传递原始头信息
proxy_set_header Origin $http_origin;
proxy_set_header Access-Control-Request-Method $http_access_control_request_method;
proxy_set_header Access-Control-Request-Headers $http_access_control_request_headers;
# 清除上游可能设置的冲突头
proxy_hide_header 'Access-Control-Allow-Origin';
proxy_hide_header 'Access-Control-Allow-Methods';
# 重新设置正确的头
add_header 'Access-Control-Allow-Origin' $cors_origin;
}
nginx复制map $http_origin $cors_origin {
default "";
"~^https://([a-z0-9-]+\.)?example\.(com|net)(:[0-9]+)?$" $http_origin;
"~^http://(localhost|127\.0\.0\.1)(:[0-9]+)?$" $http_origin;
}
nginx复制limit_req_zone $http_origin zone=cors:10m rate=10r/s;
location /api/ {
limit_req zone=cors burst=20 nodelay;
# ...其他配置
}
nginx复制location /api/admin/ {
# 限制管理接口只能内网访问
allow 10.0.0.0/8;
deny all;
# 覆盖通用CORS设置
add_header 'Access-Control-Allow-Origin' 'https://admin.example.com';
add_header 'Access-Control-Allow-Credentials' 'true';
}
在大型金融项目中,我们曾通过Nginx层精细化的跨域控制,实现了: