Nginx反向代理上游服务器时Django出现CORS跨域问题,直接访问上游服务器则正常
遇到这种情况真的很挠头——明明直接访问上游服务器的IP或域名一切正常,通过Nginx反向代理就触发CORS错误,而且还是同一个应用部署,这大概率是代理过程中请求头传递+Django的Host验证在搞鬼,咱们一步步拆解解决:
核心原因分析
你目前的问题本质上可能不是CORS中间件的问题,而是:
当通过Nginx代理时,你配置了proxy_set_header Host $http_host;,这会把前端请求的api.example.com作为Host头传递给上游的Django服务器。但上游Django的ALLOWED_HOSTS里只加了api3.example.com和1.2.3.4,并没有包含api.example.com。
这时候Django会因为Host不在允许列表里,直接返回错误响应(你看到的404或400),而这个错误响应不会经过你的CORS中间件,自然就没有Access-Control-Allow-Origin这类跨域头,浏览器就会把这个错误识别为CORS问题。
而你直接访问上游服务器时,请求的Host是api3.example.com/1.2.3.4,正好在Django的允许列表里,所以应用正常处理请求,CORS中间件也能正确返回跨域头,自然就没问题。
解决方案步骤
1. 先解决Django的Host允许问题(最关键)
修改上游Django项目的settings.py,把代理域名api.example.com添加到ALLOWED_HOSTS里:
ALLOWED_HOSTS = ['api3.example.com', '1.2.3.4', 'api.example.com'] # 测试环境可以临时用 '*' 验证,但生产环境不建议这么做
修改后重启Django应用(比如uwsgi/gunicorn),再测试代理请求。
2. 优化Nginx的代理配置,确保请求头完整传递
在Nginx的location /块里,添加显式传递Origin头的配置,避免上游Django拿不到跨域请求的Origin信息:
location / { proxy_pass http://backend; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Scheme $scheme; proxy_set_header REMOTE_ADDR $remote_addr; # 新增:传递Origin头给上游 proxy_set_header Origin $http_origin; proxy_read_timeout 1000; }
3. 可选:在Nginx层面直接处理CORS(更稳定)
如果你想彻底避免Django中间件的影响,可以在Nginx的server块里直接配置CORS规则,让Nginx来处理跨域请求,这样代理层就能直接响应OPTIONS预检请求,不需要透传给上游:
server { server_name api.example.com; # ... 你的其他配置(gzip、ssl等) # 先处理OPTIONS预检请求 if ($request_method = OPTIONS) { add_header Access-Control-Allow-Origin "*"; add_header Access-Control-Allow-Methods "DELETE, GET, OPTIONS, PATCH, POST, PUT"; add_header Access-Control-Allow-Headers "cache-control, accept, accept-encoding, authorization, content-type, dnt, origin, user-agent, x-csrftoken, x-requested-with"; add_header Access-Control-Max-Age 86400; add_header Content-Length 0; add_header Content-Type text/plain; return 200; } # 给其他请求添加CORS响应头 add_header Access-Control-Allow-Origin "*"; add_header Access-Control-Allow-Methods "DELETE, GET, OPTIONS, PATCH, POST, PUT"; add_header Access-Control-Allow-Headers "cache-control, accept, accept-encoding, authorization, content-type, dnt, origin, user-agent, x-csrftoken, x-requested-with"; # ... 你的location配置 }
注意:Nginx里的if指令在某些场景下有坑,但处理OPTIONS请求这个场景是安全的。
验证步骤
- 先完成步骤1,重启Django后测试代理请求,看是否还会出现CORS错误;
- 如果还有问题,再添加步骤2的Origin头配置;
- 若还是不行,再尝试步骤3的Nginx层面CORS配置。
备注:内容来源于stack exchange,提问作者nadermx

