最简方式是在任何输出前用header()设置cors头;需注意位置、credentials限制、预检options处理、输出缓冲干扰及生产环境白名单校验。

PHP中设置Access-Control-Allow-Origin头最简方式
直接在响应前输出CORS头即可生效,不需要框架或扩展。关键不是“怎么加”,而是“加在哪儿”——必须在任何内容输出(包括空格、BOM)之前调用header(),否则会报Cannot modify header information错误。
常见错误现象:页面白屏、控制台显示No 'Access-Control-Allow-Origin' header、后端日志里没报错但前端拿不到数据。
- 确保
header('Access-Control-Allow-Origin: *');出现在echo、print、HTML输出、甚至文件开头BOM之前 - 如果前端带
Credentials(比如fetch(..., { credentials: 'include' })),Access-Control-Allow-Origin不能为*,得写具体域名,例如https://example.com - 配合
Access-Control-Allow-Credentials: true时,后端必须同时设置Access-Control-Allow-Origin为非通配符值,否则浏览器直接拒绝响应
处理预检请求(OPTIONS)的必要条件
当请求含自定义头(如Authorization)、使用PUT/DELETE方法、或Content-Type不是application/x-www-form-urlencoded等“简单类型”时,浏览器会先发一个OPTIONS请求探路。PHP不自动响应它,必须手动拦截并返回204。
典型表现:Chrome Network里看到OPTIONS状态码是404或500,后续真实请求根本不发出。
立即学习“PHP免费学习笔记(深入)”;
- 检查
$_SERVER['REQUEST_METHOD'] === 'OPTIONS',如果是,立即header('HTTP/1.1 204 No Content')并exit - 预检响应里至少要包含
Access-Control-Allow-Methods(如GET, POST, PUT, DELETE)和Access-Control-Allow-Headers(如Content-Type, Authorization) - 不要在
OPTIONS响应里输出任何body,连换行都不行,否则部分浏览器(特别是Safari)会失败
header()与输出缓冲(ob_start)的兼容性陷阱
有些环境(如启用了output_buffering的PHP配置,或某些CMS模板机制)会让header()看似成功,实则被缓冲吞掉。这时候CORS头根本没发出去,但PHP也不报错。
验证方式:用curl -I http://your-api.com/endpoint看响应头里有没有Access-Control-Allow-Origin,别只信浏览器开发者工具里的“Network → Headers”标签页——它可能缓存了旧响应。
- 上线前务必用
curl -I或Postman直连后端接口验证响应头 - 如果用了
ob_start(),确保在ob_end_flush()或ob_end_clean()之前已调用header() - 某些共享主机禁用
header()函数,此时只能靠Web服务器配置(如Nginx的add_header)补救,PHP层无解
生产环境必须限制Access-Control-Allow-Origin值
开发时用*省事,但上线后放开所有域名等于把API白送出去。尤其当接口涉及用户数据或登录态,credentials: true + Access-Control-Allow-Origin: *是明确禁止的组合,PHP会直接拒绝发送该头(现代PHP版本会警告)。
最容易被忽略的一点:域名匹配要严格,https://api.example.com和https://www.example.com是两个不同源,不能靠模糊匹配。
- 从
$_SERVER['HTTP_ORIGIN']取值做白名单校验,只对合法域名回传对应Origin头 - 注意
HTTP_ORIGIN可能为空(比如本地file://打开的HTML),需判空再比较 - 避免用
parse_url()提取host后直接in_array()比对,应统一小写、去掉端口(除非特别需要)、排除协议











