React Native Web对接EC2上Nginx+Django服务出现CORS错误如何解决?
核心问题排查
1. 前端请求配置错误
CORS 相关的Access-Control-Allow-*头是服务端返回的响应头,前端请求中主动添加这些头属于错误操作,会被浏览器视为自定义请求头,触发额外的预检规则,甚至直接导致校验失败。
同时你开启了凭据传递,根据浏览器CORS规则,这种情况下服务端的Access-Control-Allow-Origin不能设置为*,必须返回和请求Origin完全匹配的域名,否则校验直接不通过。
另外你混淆了不同请求库的参数:withCredentials是XHR/axios的凭据参数,fetch API对应的参数是credentials: "include",参数写错也会导致凭据传递失效。
2. Nginx配置冲突&规则错误
你同时在Nginx和Django两层配置CORS规则,且Nginx做了如下错误配置:
proxy_hide_header Access-Control-Allow-Origin会先把Django返回的合法CORS头删除- 自行添加的
Access-Control-Allow-Origin '*'和凭据传递要求冲突 - 仅给
Access-Control-Allow-Credentials加了always参数,其他CORS头默认只在200/201等状态码下返回,OPTIONS预检请求(通常返回200/204)不会携带这些头,直接导致预检失败。
3. Django配置冗余冲突
你同时开启了CORS_ORIGIN_ALLOW_ALL = True、CORS_ALLOWED_ORIGINS = ['*']、CORS_ALLOWED_ORIGIN_REGEXES三个规则,且未适配凭据传递的要求,虽然django-cors-headers在CORS_ORIGIN_ALLOW_ALL = True且开启凭据支持时会自动返回请求对应的Origin值,但被上层Nginx覆盖为*,完全失效。
修复步骤
步骤1:清理前端请求代码
删除所有Access-Control-Allow-开头的请求头,调整后代码如下:
fetch(`https://${env.apiUrl}/api/v1/user`, { method: "GET", mode: "cors", credentials: "include", headers: { Authorization: await getToken(), }, })
步骤2:简化Nginx配置,删除CORS相关配置
推荐直接用django-cors-headers处理CORS规则,更灵活不易出错,调整Nginx配置如下:
location / { proxy_pass http://web/; proxy_redirect off; 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; # 让Django识别请求协议,避免cors头返回协议错误 }
步骤3:修正Django CORS配置
删除冗余配置,明确允许的前端域名,适配凭据要求:
MIDDLEWARE = [ 'corsheaders.middleware.CorsMiddleware', 'django.middleware.common.CommonMiddleware', # 注意CorsMiddleware必须放在CommonMiddleware之前 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', .... ] # 关闭全局允许,明确指定允许的域名 CORS_ORIGIN_ALLOW_ALL = False # 按实际情况添加前端域名,协议、域名、端口必须完全匹配,多个域名用逗号分隔 CORS_ALLOWED_ORIGINS = [ 'http://localhost:19006', # 本地RN Web的调试地址 'https://你的线上前端域名.com' ] ALLOWED_HOSTS = ['*'] CORS_ALLOW_CREDENTIALS = True # 剩下的CORS_ALLOW_METHODS、CORS_ALLOW_HEADERS保持原有配置即可,不需要的规则可直接删除
步骤4:验证配置
重启Nginx和Django服务后,用curl重新发起OPTIONS请求,检查响应头中是否存在Access-Control-Allow-Origin,值是否和你传入的Origin完全一致,且Access-Control-Allow-Credentials为true。
可选:Nginx直接处理CORS的配置参考
如果不想用Django处理CORS,可直接用Nginx配置,无需保留Django的django-cors-headers相关配置,Nginx配置参考如下:
location / { # 处理预检请求 if ($request_method = OPTIONS) { add_header Access-Control-Allow-Origin $http_origin always; add_header Access-Control-Allow-Credentials true always; add_header Access-Control-Allow-Methods "GET,PUT,POST,DELETE,OPTIONS" always; add_header Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept, Authorization" always; return 204; } # 普通请求加头 add_header Access-Control-Allow-Origin $http_origin always; add_header Access-Control-Allow-Credentials true always; proxy_pass http://web/; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; }
内容的提问来源于stack exchange,提问作者Yoonseok Choi

