一、什么是跨域?
跨域(Cross-Origin)是指浏览器同源策略(Same-Origin Policy)限制下的行为。当网页尝试从不同源(协议、域名、端口任一不同)请求资源时,就会发生跨域问题。
跨域 = 浏览器的安全规则:不允许一个网站,随便请求另一个网站的数据。
同源的定义:
- 协议相同(http/https)
- 域名相同(example.com)
- 端口相同(80/443)
跨域示例:
- http://a.com → http://b.com (域名不同)
- http://a.com:8080 → http://a.com:9090 (端口不同)
- http://a.com → https://a.com (协议不同)
比如:
- 前端页面在 http://localhost:8080
- 后端接口在 http://localhost:9090
浏览器直接拦截请求,报:
No 'Access-Control-Allow-Origin' header is present on the requested resource
这就是 跨域问题。
二、Nginx 跨域配置
1. 基础跨域配置
location /api/ { # 允许所有域名访问(生产环境建议指定具体域名) add_header 'Access-Control-Allow-Origin''*' always; # 允许的请求方法 add_header 'Access-Control-Allow-Methods''GET, POST, PUT, DELETE, OPTIONS' always; # 允许的请求头 add_header 'Access-Control-Allow-Headers''DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always; # 允许携带凭证(Cookie) add_header 'Access-Control-Allow-Credentials''true' always; # 预检请求缓存时间(单位:秒) add_header 'Access-Control-Max-Age'1728000 always; # 处理 OPTIONS 预检请求 if ($request_method='OPTIONS') { return 204; } proxy_pass http://backend_server;}2. 动态域名配置(推荐)

# 允许特定域名跨域location /api/ { # 获取请求源 set$origin$http_origin; # 允许的域名列表 if ($origin ~* (https?://(www\.)?(domain1\.com|domain2\.com|localhost))) { add_header 'Access-Control-Allow-Origin''$origin' always; add_header 'Access-Control-Allow-Credentials''true' always; add_header 'Access-Control-Allow-Methods''GET, POST, PUT, DELETE, OPTIONS' always; add_header 'Access-Control-Allow-Headers''DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always; } if ($request_method='OPTIONS') { add_header 'Access-Control-Max-Age'1728000; add_header 'Content-Type''text/plain charset=UTF-8'; add_header 'Content-Length'0; return 204; } proxy_pass http://backend_server;}3. 使用 map 模块(更优雅)
# 在 http 块中定义map $http_origin$cors_origin { default ""; "~^https?://(www\.)?(domain1\.com|domain2\.com)$" $http_origin; "~^http://localhost(:[0-9]+)?$" $http_origin;}# 在 server/location 块中使用location /api/ { add_header 'Access-Control-Allow-Origin'$cors_origin always; add_header 'Access-Control-Allow-Credentials''true' always; add_header 'Access-Control-Allow-Methods''GET, POST, PUT, DELETE, OPTIONS' always; add_header 'Access-Control-Allow-Headers''Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,X-Requested-With' always; if ($request_method='OPTIONS') { return 204; } proxy_pass http://backend_server;}三、常见场景配置
1. 静态资源跨域
location /static/ { add_header 'Access-Control-Allow-Origin''*' always; alias /var/www/static/;}2. 全局跨域配置
server { listen 80; server_name example.com; # 全局添加跨域头 add_header 'Access-Control-Allow-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' always; location / { root /var/www/html; try_files $uri$uri/ /index.html; }}3. 前后端分离项目配置
server { listen 80; server_name frontend.com; # 前端静态资源 location / { root /var/www/frontend; try_files $uri$uri/ /index.html; } # 后端 API 代理 location /api/ { add_header 'Access-Control-Allow-Origin''http://frontend.com' always; add_header 'Access-Control-Allow-Credentials''true' always; add_header 'Access-Control-Allow-Methods''GET, POST, PUT, DELETE, OPTIONS' always; add_header 'Access-Control-Allow-Headers''Authorization,Content-Type' always; if ($request_method='OPTIONS') { return 204; } proxy_pass http://backend_server:8080/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; }}四、重要参数说明
参数 | 说明 |
Access-Control-Allow-Origin | 指定允许的源,* 表示全部,但不能与 Credentials 同时使用 |
Access-Control-Allow-Methods | 允许的 HTTP 方法 |
Access-Control-Allow-Headers | 允许的请求头字段 |
Access-Control-Allow-Credentials | 是否允许携带 Cookie |
Access-Control-Max-Age | 预检请求的缓存时间 |
Access-Control-Expose-Headers | 允许客户端访问的响应头 |
五、注意事项
1.always 参数:确保即使响应状态码为 4xx/5xx 时也添加跨域头
2.\* 与凭证问题:当 Access-Control-Allow-Credentials: true 时,Allow-Origin 不能为 *,必须指定具体域名
3.OPTIONS 预检请求:
- 非简单请求(如 PUT、DELETE 或带自定义头)会先发送 OPTIONS 预检
- 必须正确响应 OPTIONS 请求,返回 204 状态码
4.性能优化:
- 使用 Access-Control-Max-Age 缓存预检结果
- 避免在 if 块中使用 proxy_pass(if 是限制性上下文)
5.安全建议:
- 生产环境避免使用 *,应指定具体的白名单域名
- 使用 HTTPS 避免中间人攻击
- 谨慎使用 Access-Control-Allow-Credentials
六、调试方法
# 查看响应头curl-I-H"Origin: http://example.com" http://your-domain.com/api/test# 测试 OPTIONS 请求curl-X OPTIONS -H"Origin: http://example.com"-H"Access-Control-Request-Method: POST" http://your-domain.com/api/test浏览器控制台错误信息:
- No 'Access-Control-Allow-Origin' header → 缺少跨域头
- Credentials flag is 'true' → 凭证配置问题
- Credentials flag is 'true' → 凭证配置问题
七、完整示例
http { # 定义允许的源 map $http_origin$allowed_origin { ~^https?://(www\.)?(trusted-domain\.com|app\.example\.com)$ $http_origin; ~^http://localhost(:[0-9]+)?$ $http_origin; default ""; } server { listen 80; server_name api.example.com; location /api/ { # 添加跨域头 add_header 'Access-Control-Allow-Origin'$allowed_origin always; add_header 'Access-Control-Allow-Credentials''true' always; add_header 'Access-Control-Allow-Methods''GET, POST, PUT, DELETE, OPTIONS, PATCH' always; add_header 'Access-Control-Allow-Headers''Authorization, Content-Type, X-Requested-With, Accept' always; add_header 'Access-Control-Expose-Headers''Content-Length, X-Total-Count' always; add_header 'Access-Control-Max-Age'3600 always; # 处理预检请求 if ($request_method='OPTIONS') { add_header 'Content-Length'0; add_header 'Content-Type''text/plain charset=UTF-8'; return 204; } # 代理到后端服务 proxy_pass http://backend_upstream; 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 X-Forwarded-Proto $scheme; } }}一句话总结
- 跨域:浏览器的安全限制,不同域名 / 端口不能互相请求。
- Nginx 跨域:用 Nginx 配置,绕开或解除这个限制,让前后端能正常通信。