access-control-allow-methods仅对预检请求生效,需在options响应中显式设置;普通get/post请求不读取该头,且web服务器层配置比php更稳定可靠。

PHP中设置Access-Control-Allow-Methods只对预检请求生效
浏览器发跨域请求时,如果带了自定义头、用了PUT/DELETE方法,或Content-Type不是application/x-www-form-urlencoded、multipart/form-data、text/plain这三种之一,就会先发一个OPTIONS预检请求。这时候你写的Access-Control-Allow-Methods才起作用——普通GET/POST请求根本不会读这个头。
常见错误:在if ($_SERVER['REQUEST_METHOD'] === 'GET')分支里加header('Access-Control-Allow-Methods: GET');,结果发现POST还是能通,或者OPTIONS直接405报错。原因就是没单独处理OPTIONS请求。
- 必须显式响应
OPTIONS请求,至少返回200和必要CORS头 -
Access-Control-Allow-Methods应放在OPTIONS响应里,而不是业务逻辑分支中 - 如果用Nginx/Apache做反向代理,PHP脚本可能根本收不到
OPTIONS,得在Web服务器层配置
只允许GET和POST的完整PHP写法
不能靠$_SERVER['REQUEST_METHOD']判断后动态输出头,而要在所有请求入口统一加基础CORS头,并对OPTIONS做短路响应。
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
header('Access-Control-Allow-Origin: https://example.com');
header('Access-Control-Allow-Methods: GET, POST');
header('Access-Control-Allow-Headers: Content-Type, X-Requested-With');
header('Access-Control-Allow-Credentials: true');
exit(0);
}
header('Access-Control-Allow-Origin: https://example.com');
header('Access-Control-Allow-Methods: GET, POST');
header('Access-Control-Allow-Headers: Content-Type, X-Requested-With');
header('Access-Control-Allow-Credentials: true');
注意:Access-Control-Allow-Origin不能设为*同时又开Access-Control-Allow-Credentials: true,否则浏览器直接拒绝。如果要带cookie,必须写明确域名。
立即学习“PHP免费学习笔记(深入)”;
为什么用in_array($_SERVER['REQUEST_METHOD'], ['GET', 'POST'])拦不住跨域请求
这个判断只能拦住PHP执行逻辑,但浏览器跨域策略由响应头控制,不是由PHP是否执行完决定的。即使你用die()终止了GET以外的方法,只要响应里没正确设置Access-Control-Allow-Methods,浏览器依然可能因预检失败而中断后续请求。
- 前端发PUT请求 → 浏览器先发OPTIONS → 服务端没响应OPTIONS → 预检失败 → 前端JS拿不到任何响应,连
405都看不到 - 想“禁止PUT跨域”,关键不是不让PHP跑PUT逻辑,而是让OPTIONS响应里不包含
PUT - 真正需要拦截的是非法方法的业务执行,那应该放在主逻辑里,和CORS头配置是两件事
Apache/Nginx里配比PHP里配更可靠
PHP脚本一旦出错(比如语法错误、超时、未捕获异常),CORS头就发不出去,跨域直接崩。而Web服务器层的响应头是稳定下发的。
Apache示例(.htaccess):
<IfModule mod_headers.c>
SetEnvIf Origin "https://example\.com$" origin_is=$0
Header always set Access-Control-Allow-Origin %{origin_is}e env=origin_is
Header always set Access-Control-Allow-Methods "GET, POST"
Header always set Access-Control-Allow-Headers "Content-Type, X-Requested-With"
Header always set Access-Control-Allow-Credentials "true"
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=200,L]
</IfModule>
Nginx示例(server块内):
add_header 'Access-Control-Allow-Origin' 'https://example.com';
add_header 'Access-Control-Allow-Methods' 'GET, POST';
add_header 'Access-Control-Allow-Headers' 'Content-Type, X-Requested-With';
add_header 'Access-Control-Allow-Credentials' 'true';
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
Web服务器配置没法动态判断来源域名,所以Access-Control-Allow-Origin通常写死或靠正则匹配;PHP里反而能做白名单校验,但稳定性差一截。实际项目里,建议Web服务器管基础头,PHP只做细粒度校验(比如检查Origin是否在数据库白名单里)。











