thinkphp 的 redirect() 方法第三个参数为 http 状态码,默认302;传入非法码(如3)会导致跳转失败,因http无“3”状态码;必须确保无前置输出,api中禁用redirect()而应使用response()->json()->code()。

redirect() 第三个参数就是状态码
ThinkPHP 的 redirect() 方法从 TP5.1 起明确支持通过第三个参数传入 HTTP 状态码,默认是 302。它不是靠额外函数或 header 手动设置,而是直接由该参数控制底层 Location 响应头的 status 行。
常见误用是把延时值(如 3)错当状态码传进去,结果变成 303 跳转(HTTP/1.1 规范中 303 强制 GET),导致 POST 请求被丢弃、表单重复提交等意外行为。
-
$this->redirect('user/login', [], 301)→ 永久重定向,搜索引擎会迁移权重 -
$this->redirect('admin/dashboard', ['tab' => 'log'], 307)→ 临时重定向且保留原始请求方法(适合 POST 跳转) -
$this->redirect('https://new.example.com', [], 308)→ 永久重定向且保留方法(比 301 更严格)
注意:状态码必须是整数,不能是字符串;若传了非标准码(如 600),PHP 不报错但客户端可能忽略或降级处理。
为什么 redirect(…, 3) 在 TP6 里跳转失败
因为 3 被当成状态码,而 HTTP 中没有 3xx 以外的「3」状态码——实际发出的是 HTTP/1.1 3 Not Found 这类非法响应,浏览器拒绝解析,表现为 URL 不变、控制台无错误、页面静默卡住。
老项目文档常把延时写成第三个参数,但 TP6+ 已移除对 delay 的原生支持。延时跳转必须交给前端做:
- 模板中用
{:url('user/profile', ['id' => 123])}生成链接,再配合 JSsetTimeout(() => location.href = ..., 3000) - 或者返回 JSON 响应,让前端自己决定是否延时跳转
别在控制器里试图用 redirect() 实现“3 秒后跳转”,那不是它的设计职责。
redirect() 设置状态码的前提:不能有任何前置输出
redirect() 底层调用 header("Location: ...", true, $code),一旦 PHP 缓冲区已有内容(哪怕一个空格、BOM、echo、var_dump 或调试模式下的 Trace 输出),就会触发 headers already sent 错误,且 redirect() 会静默失效——不报错、不跳转、也不终止脚本。
排查要点:
- 检查控制器方法开头是否有
echo、print_r、dd()等调试语句 - 确认没开启
display_errors = On导致错误信息提前刷出 - 查看中间件是否提前调用了
Response::create()->send()或exit - 验证文件编码是否为 UTF-8 无 BOM(尤其 Windows 编辑器易带 BOM)
TP 框架本身不会帮你清理已输出内容,它只负责发头;头发不出去,就什么都不会发生。
API 接口里别用 redirect() 设状态码
接口返回 JSON,不是 HTML 页面。redirect() 强制返回空 body + 302 响应头,前端 fetch 默认不跟随重定向(redirect: 'follow' 需显式开启),而且 Axios 等库收到 302 后通常直接 resolve 成空响应,前端拿不到业务数据。
正确做法是用 response()->json($data)->code($httpCode),例如:
return response()->json(['msg' => '未登录'])->code(401);
如果项目大量使用 json() 助手函数,记得它默认固定返回 200,要改状态码必须绕过它——要么换用 response()->json()->code(),要么自己封装一个 json_with_code()。
状态码和跳转是两套逻辑:一个是告诉客户端“你该去哪”,另一个是告诉客户端“这次请求结果如何”。混用只会让前后端都困惑。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











