thinkphp5跨域需自定义中间件拦截options预检请求并返回204响应,同时设置全部cors头;启用credentials时origin必须动态白名单校验,不可用*。

ThinkPHP5 默认不处理跨域,加了 header() 也拦不住浏览器报错,根本原因几乎全是没响应 OPTIONS 预检请求——不是头没设对,是压根没让预检通过。
为什么 header() 设了还是被拦截?
常见错误是只在控制器或中间件末尾写 header('Access-Control-Allow-Origin: *'),但浏览器发 POST/带 Authorization 的请求前,必先发一个 OPTIONS 请求。TP5 不自动拦截它,若后端没返回 204 状态码 + 完整 CORS 头,整个请求就卡死,控制台只显示 “CORS error”,不报具体哪行出问题。
-
header()必须在任何输出(包括空格、BOM、echo)之前调用,否则触发 “headers already sent” 错误 - 中间件里用
$response->header()时,必须在return $next($request)之后设置,否则响应体已生成,头被丢弃 - 若前端配了
credentials: true(比如传 Cookie),Access-Control-Allow-Origin不能为*,必须动态读取$request->header('origin')并白名单校验
自定义中间件怎么写才真正生效?
中间件必须在 handle() 开头判断是否为 OPTIONS 请求,并立即返回空响应;所有 CORS 头都要在返回前写入,不能依赖 $next() 后的 $response 对象。
- 执行
php think make:middleware CorsMiddleware生成类 - 编辑
app/middleware/CorsMiddleware.php,核心逻辑如下: - 开头加
if ($request->isOptions()) { return response('', 204); } - 在该
return之前设置全部 header,例如:header('Access-Control-Allow-Origin: ' . $origin)、header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS')、header('Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With') - 不要在
$next($request)之后再设header(),TP5 的响应流此时已不可逆
路由级 or 控制器级?哪种更靠谱?
TP5 没有官方 allowCrossDomain() 方法(那是 TP6 的),所以路由级配置只能靠手动绑定中间件或行为(behavior),不如中间件直接可靠。
- 控制器内设
header()可行,但需每处都加,且同样要处理 OPTIONS,容易漏 - 行为(behavior)方式(如
application/api/behavior/CORS.php)可统一挂到app_init,但依赖$_SERVER变量,在 CLI 或某些 SAPI 下可能失效 - 最稳方案仍是自定义中间件:覆盖全站、位置可控、能精准拦截 OPTIONS、支持白名单 Origin 动态回写
最容易被忽略的点是:启用凭证时,Access-Control-Allow-Origin 和 Access-Control-Allow-Credentials: true 必须同时存在且 Origin 不能为 *——浏览器看到 * 就直接丢弃响应,连控制台都不报错,调试时毫无线索。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











