php中header()无效常因nginx拦截,需在location块用add_header … always;配置跨域头,并单独处理options预检请求,统一收口定义、透传origin、逐层curl验证。

PHP脚本里加header没用?先确认Nginx有没有拦住
很多同学在index.php开头写了header('access-Control-Allow-Origin: *');,但浏览器还是报CORS Error——问题往往不在PHP,而在Nginx根本没把那个header透给前端。Nginx默认会过滤掉部分自定义响应头,尤其当它自己也返回了同名头(比如Content-Type)时,还可能覆盖PHP输出的跨域头。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 在Nginx配置的
location ~ .php$块里,加上add_header Access-Control-Allow-Origin "*" always;(注意always参数,否则仅对2xx/3xx生效) - 如果需要带凭证(
withCredentials: true),必须写具体域名,不能用*,同时补上add_header Access-Control-Allow-Credentials "true" always; - 别忘了允许的请求方法和头字段:
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";和add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,if-Modified-Since,Cache-Control,Content-Type,Range,Authorization";
遇到OPTIONS预检失败?Nginx要单独处理preflight
浏览器发PUT、delete或带自定义头的请求前,会先发一个OPTIONS请求。这个请求根本不会进PHP,Nginx必须自己响应该预检,否则直接502或405。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 在
location ~ .php$同级加一个location = /或更精准的location ^~ /api/块,里面只处理OPTIONS - 用
if ($request_method = 'OPTIONS') { add_header Access-Control-Allow-Origin "*"; add_header Access-Control-Allow-Methods "GET, POST, OPTIONS"; add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization"; add_header Access-Control-Max-Age 1728000; add_header Content-Length 0; add_header Content-Type text/plain; return 204; } - 别用
rewrite或proxy_pass去转交OPTIONS——PHP通常没配OPTIONS路由,会404
add_header不生效?检查Nginx继承规则和重复定义
Nginx的add_header不向下继承,且同级多个add_header指令中,只有最后一个生效(不是叠加)。常见坑是:在server块写了通配跨域头,又在location里写了另一个,结果location里的覆盖了server里的,而你没注意到。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 把跨域相关
add_header统一收口到最内层location,避免分散定义 - 用
curl -I http://yourdomain.com/test.php实测响应头,别只信浏览器开发者工具(缓存/重定向可能干扰) - 如果用了
fastcgi_pass,确认没在fastcgi_params里误删了HTTP_ORIGIN等变量——虽然跨域头不依赖它,但某些PHP框架会读这个判断是否走CORS逻辑
为什么本地开发正常,上线就跨域失败?查proxy_set_header和https重定向
生产环境常套CDN或反向代理(如阿里云SLB、Nginx前置机),这时真实请求头可能被改写。典型表现:Nginx日志里$http_origin为空,或者https站被强制跳转成http再回来,导致Origin变成http://而服务端只认https://。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 在代理层(比如前置Nginx)加
proxy_set_header Origin $http_origin;,确保原始Origin透传 - 如果启用了
return 301 https://$host$request_uri;,检查是否在重定向前已返回了跨域头——301响应本身不带Access-Control-Allow-Origin,浏览器直接拒绝后续请求 - 用
curl -H "Origin: https://example.com" -I http://your-api.com/endpoint模拟跨域请求,看响应头是否完整
跨域真正麻烦的从来不是加几行header,而是每一层网络组件(浏览器→CDN→负载均衡→Nginx→PHP)都可能悄悄吃掉、覆盖或忽略某个头。调试时得一层层curl过去,而不是盯着PHP文件猛敲echo。