只有POST表单才能伪造方法,因为Laravel的Request::getMethod()底层强制要求原始HTTP方法为POST才解析_method字段;若非POST(如GET),直接返回原方法,不处理_method,以防止语义篡改和保障REST安全。

HTML 原生表单只支持 GET 和 POST,想用 PUT、PATCH 或 DELETE 必须靠 Laravel 的方法伪造(Method Spoofing)机制,它不是“绕过限制”,而是框架主动识别并转换请求方式。
为什么只有 POST 表单才能伪造方法?
Laravel 的 Request::getMethod() 在底层做了硬性判断:只有当原始 HTTP 方法是 POST 时,才会去检查 _method 字段。其他方法(比如直接用 GET 提交 _method=PUT)会被忽略,直接返回 GET。
这是因为:
- 安全考虑——防止 GET 请求被恶意篡改语义(如通过 URL 参数伪造 DELETE)
- 语义一致——RESTful 设计中,非幂等操作(PUT/PATCH/DELETE)本就不该由可缓存、可书签的 GET 触发
- 源码逻辑明确:
if ('POST' !== $this->method) { return $this->method; }
@method() 和 _method 隐藏字段怎么写才有效?
必须同时满足两个条件:表单 method="POST" + 包含合法的 _method 值。Blade 中推荐用 @method(),它会自动渲染为标准隐藏字段。
正确写法示例:
<form action="/users/1" method="POST">
@csrf
@method('PUT')
<button type="submit">Update</button>
</form>
等价于手动写:
<form action="/users/1" method="POST">
<input type="hidden" name="_token" value="xxx">
<input type="hidden" name="_method" value="PUT">
<button type="submit">Update</button>
</form>
注意以下常见错误:
- 漏掉
@csrf→ 触发 419 Page Expired 错误 -
name="_method"拼错(如写成method或_METHOD)→ 无效,仍按 POST 处理 - 值不是大写字符串(如
put或"put")→ 被拒绝,抛出SuspiciousOperationException - 路由未定义对应
PUT动作 → 405 Method Not Allowed
伪造方法在中间件和验证链中何时生效?
_method 的解析发生在请求进入路由分发前,具体是在 Illuminate\Http\Request 的 getMethod() 第一次被调用时(通常是路由匹配阶段)。这意味着:
- CSRF 验证(
VerifyCsrfToken)看到的是原始POST请求,但它不关心_method,只校验_token - 自定义中间件若在
getMethod()之前读取$request->method(),拿到的是POST;之后读取才是伪造后的值(如PUT) - 控制器里用
$request->isMethod('put')或$request->method() === 'PUT'是安全的,因为此时已转换完毕
如果你在中间件里需要提前判断最终方法,别直接读 $_SERVER['REQUEST_METHOD'],而应调用 $request->method() 触发转换逻辑。
哪些情况会让方法伪造失效?
最常被忽略的其实是请求头和配置干扰:
- 前端发 Ajax 请求时,手动设置了
X-HTTP-Method-Override: PUT,但没关掉self::$httpMethodParameterOverride(Laravel 默认开启,所以仍会优先生效) - 服务器 Nginx/Apache 重写了
REQUEST_METHOD,导致原始值不是POST - PHP 运行模式为 CGI/FastCGI 时,
$_SERVER['REQUEST_METHOD']可能被污染,需确认其值为POST - 使用
FormRequest类做验证时,若重写了getValidatorInstance()并提前访问了method(),可能触发过早解析
调试时最直接的方式是 dump $request->server->get('REQUEST_METHOD') 和 $request->method() 对比,看是否发生了转换。


















