ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Nginx解决跨域问题的完整配置指南

Nginx解决跨域问题的完整配置指南

1. 跨域问题的本质与Nginx解决方案

前端开发中最让人头疼的问题之一就是跨域限制。当你的前端应用运行在http://localhost:8080,而API服务部署在http://api.example.com时,浏览器会直接阻止这种跨域请求。这不是bug,而是浏览器出于安全考虑设计的同源策略(Same-Origin Policy)。

同源策略要求协议、域名和端口三者完全相同才允许直接通信。现代前后端分离架构中,前端静态资源与后端API服务往往部署在不同域名下,这就产生了跨域问题。常见的错误提示是:

Access to XMLHttpRequest at 'http://api.example.com/data' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Nginx作为高性能的反向代理服务器,可以通过配置CORS(跨域资源共享)规则来解决这个问题。相比在前端使用JSONP等方案,Nginx方案有以下优势:

  • 不依赖前端代码修改
  • 支持所有HTTP方法
  • 可以精细控制允许的域名、方法和头部
  • 性能开销几乎可以忽略

2. Nginx基础CORS配置

最简单的CORS配置是在Nginx的server或location块中添加以下指令:

add_header Access-Control-Allow-Origin "*"; add_header Access-Control-Allow-Methods "GET, POST, OPTIONS"; add_header Access-Control-Allow-Headers "Content-Type";

这个配置表示:

  • 允许所有域名访问(*)
  • 允许GET、POST和OPTIONS方法
  • 允许Content-Type请求头

注意:生产环境不建议使用*作为允许的域名,这会导致安全风险。应该明确指定允许的域名。

3. 生产环境推荐配置

对于生产环境,我们需要更精细的控制。下面是一个完整的配置示例:

server { listen 80; server_name api.example.com; # 根据请求来源动态设置允许的域名 set $cors_origin ""; if ($http_origin ~* "^https://www.example.com$") { set $cors_origin $http_origin; } if ($http_origin ~* "^https://staging.example.com$") { set $cors_origin $http_origin; } location / { # 处理预检请求(OPTIONS) if ($request_method = OPTIONS) { add_header Access-Control-Allow-Origin $cors_origin; add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"; add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-Requested-With"; add_header Access-Control-Max-Age 1728000; # 预检请求缓存20天 add_header Content-Type "text/plain; charset=UTF-8"; add_header Content-Length 0; return 204; } # 处理实际请求 add_header Access-Control-Allow-Origin $cors_origin; add_header Access-Control-Allow-Credentials "true"; add_header Access-Control-Expose-Headers "Content-Length, X-Total-Count"; # 你的代理配置 proxy_pass http://backend; } }

这个配置实现了:

  1. 动态允许特定域名(www和staging环境)
  2. 正确处理OPTIONS预检请求
  3. 允许携带凭证(cookie等)
  4. 暴露自定义响应头
  5. 预检请求结果缓存20天

4. 常见问题与解决方案

4.1 配置不生效的可能原因

  1. 缓存问题:浏览器可能会缓存CORS响应,修改配置后记得清除缓存
  2. 配置位置错误:add_header必须放在server或location块中,不能放在http块
  3. 多个add_header冲突:同一指令的多个add_header只有最后一个会生效
  4. Nginx reload未生效:修改配置后需要执行nginx -s reload

4.2 特殊场景处理

带凭证的请求:当请求需要携带cookie时,需要特殊处理:

add_header Access-Control-Allow-Credentials "true"; add_header Access-Control-Allow-Origin $http_origin; # 不能是* add_header Access-Control-Allow-Headers "Cookie, Set-Cookie";

WebSocket跨域:WebSocket不受同源策略限制,但某些浏览器会检查Origin头:

location /socket { proxy_pass http://websocket_backend; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Origin $http_origin; }

4.3 性能优化建议

  1. 合理设置Access-Control-Max-Age减少预检请求
  2. 避免使用*作为允许的域名
  3. 合并多个add_header为一个,减少配置复杂度
  4. 对于静态资源,可以在Nginx直接设置CORS头,避免经过后端应用

5. 安全注意事项

  1. 不要过度放宽权限:只开放必要的HTTP方法和头部
  2. 验证Origin头:防止伪造Origin头的攻击
  3. 限制暴露的头部:Access-Control-Expose-Headers只暴露必要的头部
  4. HTTPS强制:生产环境应该只允许HTTPS跨域
  5. 监控异常请求:关注异常的OPTIONS请求,可能是攻击探测

6. 实际案例:前后端分离项目配置

假设我们有一个Vue前端项目(部署在https://front.example.com)和一个Node.js后端API(部署在https://api.example.com),完整的Nginx配置如下:

前端Nginx配置:

server { listen 443 ssl; server_name front.example.com; root /var/www/front/dist; index index.html; location / { try_files $uri $uri/ /index.html; } }

后端API Nginx配置:

server { listen 443 ssl; server_name api.example.com; location / { if ($request_method = OPTIONS) { add_header Access-Control-Allow-Origin "https://front.example.com"; add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"; add_header Access-Control-Allow-Headers "Authorization, Content-Type"; add_header Access-Control-Max-Age 86400; return 204; } add_header Access-Control-Allow-Origin "https://front.example.com"; add_header Access-Control-Allow-Credentials true; proxy_pass http://node_backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }

这个配置确保了:

  • 前端部署在HTTPS环境
  • 后端只接受来自前端域名的请求
  • 支持常见的REST方法
  • 允许授权和内容类型头
  • 预检请求缓存1天
  • 支持带凭证的请求

7. 调试技巧

当CORS配置不工作时,可以按以下步骤排查:

  1. 检查浏览器控制台错误信息
  2. 使用curl模拟请求:
    curl -I -X OPTIONS -H "Origin: https://front.example.com" https://api.example.com/resource
  3. 检查Nginx日志:
    tail -f /var/log/nginx/access.log
  4. 确认Nginx配置已重载:
    nginx -t && nginx -s reload
  5. 使用浏览器开发者工具查看请求和响应头

8. 进阶配置:多环境支持

对于需要支持多个环境(开发、测试、生产)的场景,可以使用Nginx的map指令:

map $http_origin $allowed_origin { default ""; "~^https://dev.example.com" $http_origin; "~^https://staging.example.com" $http_origin; "~^https://prod.example.com" $http_origin; } server { ... location / { if ($allowed_origin = "") { return 403; } if ($request_method = OPTIONS) { add_header Access-Control-Allow-Origin $allowed_origin; ... return 204; } add_header Access-Control-Allow-Origin $allowed_origin; ... } }

这种配置方式更易于维护,可以集中管理允许的域名列表。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进