
PHP里设置Access-Control-Allow-Origin不生效?
多数情况不是代码没写,而是响应头被覆盖或顺序错了。PHP的header()必须在任何输出之前调用,包括空格、BOM头、echo、print甚至文件末尾的换行。一旦有输出,header()会静默失败,浏览器收不到CORS头。
- 检查PHP文件开头有没有UTF-8 BOM(用编辑器“以UTF-8无BOM格式保存”)
- 确认没有
echo、var_dump、error_log等提前触发输出的语句 - 如果用了框架(如Laravel、ThinkPHP),别在控制器里手动
header()——框架可能已发过响应头,此时再调用会报Warning: Cannot modify header information - 用
headers_sent()快速验证:if (headers_sent($file, $line)) { die("Headers already sent in $file on line $line"); }
为什么只加Access-Control-Allow-Origin: *还不够?
当前端请求带Credentials(比如fetch(..., { credentials: 'include' })),浏览器强制要求后端不能用通配符*,必须指定确切域名,否则直接拒绝响应。
- 错误写法:
header('Access-Control-Allow-Origin: *');+credentials: 'include'→ 浏览器报No 'Access-Control-Allow-Origin' header is present - 正确写法:白名单校验
$_SERVER['HTTP_ORIGIN'],只对可信源返回对应域名:if (in_array($_SERVER['HTTP_ORIGIN'], ['https://a.com', 'https://b.net'])) { header('Access-Control-Allow-Origin: ' . $_SERVER['HTTP_ORIGIN']); } - 同时必须显式开启凭据支持:
header('Access-Control-Allow-Credentials: true'); - 注意:
Access-Control-Allow-Credentials: true和Access-Control-Allow-Origin: *不能共存,否则被浏览器忽略
预检请求(OPTIONS)被405或空白响应?
非简单请求(如带Content-Type: application/json、自定义Header、PUT/DELETE方法)会先发一个OPTIONS请求。PHP默认不处理OPTIONS,Apache/Nginx可能直接返回405,或脚本没匹配到路由而输出空白。
- 在入口PHP文件开头拦截
OPTIONS:if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { header('HTTP/1.1 200 OK'); header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS'); header('Access-Control-Allow-Headers: Content-Type, X-Requested-With, Authorization'); exit; } -
Access-Control-Allow-Headers要包含前端实际发送的自定义头,比如Authorization或X-Token,漏一个就会导致预检失败 - Apache下若用
.htaccess,需确保AllowOverride All且mod_rewrite启用;Nginx需在location块里显式允许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; charset=utf-8'; return 204; }
为什么本地开发能过,上线就跨域失败?
常见于反向代理场景:Nginx/Apache把请求转给PHP-FPM,但没透传Origin头,或PHP运行在CGI模式下$_SERVER['HTTP_ORIGIN']不可用。
立即学习“PHP免费学习笔记(深入)”;
- 检查
var_dump($_SERVER)里有没有HTTP_ORIGIN,没有的话可能是Web服务器过滤了带HTTP_前缀的头 - Nginx需配置
proxy_set_header Origin $http_origin;(如果上游需要) - 某些共享主机禁用
header()函数,或启用了output_buffering导致头延迟发送——可临时加ob_end_clean();清空缓冲区再发头 - CDN(如Cloudflare)可能缓存了不含CORS头的响应,需在缓存规则里排除
OPTIONS请求,或设置Cache-Control: no-store
真正麻烦的从来不是加几行header(),而是得一层层确认:请求到底经过了几道网关、哪一层吞了头、哪个中间件偷偷重写了响应、还有那个藏在文件末尾的不可见空格。










