php后端需在响应前用header()设置cors头,禁止前置输出;必须明确指定access-control-allow-origin(禁用*)、methods、headers及credentials,并统一处理options预检请求,同时排查nginx等中间层对响应头的覆盖或拦截。

PHP后端如何正确设置CORS响应头
移动端调用API出现Access-Control-Allow-Origin错误,本质是浏览器拦截了跨域响应,不是前端能绕过的。PHP必须在响应发出前明确声明允许来源、方法和凭证。
关键点在于:响应头必须在header()中设置,且不能有任何输出(包括空格、BOM、echo)在header()之前;否则会报“headers already sent”错误。
-
header('Access-Control-Allow-Origin: https://your-mobile-app.com');—— 生产环境禁止用*,尤其当请求带Credentials(如cookie或Authorization)时,*直接失效 -
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');—— 必须包含实际用到的HTTP方法,OPTIONS不可少,预检请求靠它 -
header('Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With');—— 前端发的自定义Header(如Authorization: Bearer xxx)必须在此显式列出 -
header('Access-Control-Allow-Credentials: true');—— 仅当前端fetch设置了credentials: 'include'时才需要,同时Access-Control-Allow-Origin不能为*
为什么手机端常遇到OPTIONS预检失败
Android WebView、iOS WKWebView 和大部分现代移动浏览器对CORS更严格,只要请求含自定义Header、非简单Content-Type(如application/json),或method不是GET/HEAD/POST,就会先发OPTIONS请求探路。PHP没处理这个请求,就直接500或404,前端卡死在预检阶段。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 在路由入口或统一中间层加判断:
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { http_response_code(200); exit; } - 不要只对
POST或GET设CORS头,OPTIONS响应也必须带上全部Access-Control-*头,否则预检失败 - 某些CDN或Nginx会拦截
OPTIONS并返回默认响应,绕过PHP。此时需检查Web服务器配置,确保OPTIONS被透传到PHP
移动端Token鉴权与CORS的冲突点
用JWT或Session Cookie做身份验证时,Authorization Header或withCredentials: true一开,CORS规则立刻收紧:Origin不能是*,且必须显式声明Access-Control-Allow-Credentials。
典型翻车场景:
- 前端发
fetch('/api/user', { headers: { Authorization: 'Bearer xxx' } })→ 后端没在Access-Control-Allow-Headers里写Authorization→ 预检拒绝 - 前端设
credentials: 'include'→ 后端Access-Control-Allow-Origin还是*→ 浏览器直接报错“Credentials not supported” - 测试用
localhost:8080调开发API → 后端只允许https://app.example.com→ 移动H5调试失败
建议按环境区分Origin白名单,例如:$allowed_origins = ['https://app.example.com', 'https://staging.app.example.com', 'https://localhost:3000'];,再用in_array($_SERVER['HTTP_ORIGIN'] ?? '', $allowed_origins)动态设置Access-Control-Allow-Origin。
PHP-FPM、Nginx与CORS头的优先级关系
很多问题其实不怪PHP代码——Nginx可能覆盖或清空了PHP设置的Header。比如Nginx配置里有add_header Access-Control-Allow-Origin "*";,但没配always参数,那它只对2xx/3xx生效,对OPTIONS的200响应可能不生效;而PHP的header()又因输出提前被忽略。
排查路径:
- 用
curl -I -X OPTIONS https://yoursite.com/api/test看真实响应头,确认哪些Header最终抵达客户端 - Nginx中若用
add_header,务必加always:例如add_header Access-Control-Allow-Origin "$http_origin" always; - PHP-FPM的
catch_workers_output = yes可帮你捕获headers already sent类错误日志,比前端报错更准 - 避免Nginx和PHP重复设同一Header(如都设
Access-Control-Allow-Origin),以Nginx为准,PHP的会被覆盖
移动端跨域真正麻烦的从来不是“怎么加头”,而是“头有没有完整、稳定、按顺序抵达浏览器”。每层代理、缓存、Web服务器都可能悄悄吃掉或改写它们。调试时别只盯着PHP文件,先抓包看最终HTTP响应。











