
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; charsetUTF-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; } }这个配置实现了动态允许特定域名(www和staging环境)正确处理OPTIONS预检请求允许携带凭证(cookie等)暴露自定义响应头预检请求结果缓存20天4. 常见问题与解决方案4.1 配置不生效的可能原因缓存问题浏览器可能会缓存CORS响应修改配置后记得清除缓存配置位置错误add_header必须放在server或location块中不能放在http块多个add_header冲突同一指令的多个add_header只有最后一个会生效Nginx reload未生效修改配置后需要执行nginx -s reload4.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 性能优化建议合理设置Access-Control-Max-Age减少预检请求避免使用*作为允许的域名合并多个add_header为一个减少配置复杂度对于静态资源可以在Nginx直接设置CORS头避免经过后端应用5. 安全注意事项不要过度放宽权限只开放必要的HTTP方法和头部验证Origin头防止伪造Origin头的攻击限制暴露的头部Access-Control-Expose-Headers只暴露必要的头部HTTPS强制生产环境应该只允许HTTPS跨域监控异常请求关注异常的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配置不工作时可以按以下步骤排查检查浏览器控制台错误信息使用curl模拟请求curl -I -X OPTIONS -H Origin: https://front.example.com https://api.example.com/resource检查Nginx日志tail -f /var/log/nginx/access.log确认Nginx配置已重载nginx -t nginx -s reload使用浏览器开发者工具查看请求和响应头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; ... } }这种配置方式更易于维护可以集中管理允许的域名列表。