Laravel『Route Method Not Allowed』の原因と対処法

Laravel『Route Method Not Allowed』の原因と対処法

Laravelの「Route Method Not Allowed」は、アクセスしたURL自体はルーティングに存在するが、送ってきたHTTPメソッド(GET/POST/PUT/PATCH/DELETE など)がそのルートで許可されていないときに発生する。例として「GETしか定義していないURLにPOSTした」「フォームはPOSTなのにルートがPUT想定」「APIはPOSTなのにブラウザからGETで叩いている」「resourceルートのどれに当たっているか勘違いしている」などが典型。まずは“どのURLに、どのメソッドで、どのルート定義がマッチしている(またはしていない)のか”を固定すると解決が速い。

エラーの出方と発生条件

典型メッセージ。

Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException
The GET method is not supported for route /posts. Supported methods: POST.

発生条件の典型:
・ルートはあるが、HTTPメソッドが違う(GET⇔POST、PUT/PATCH、DELETE)
・HTMLフォームでPUT/DELETEを送ったつもりが、実際はPOSTになっている(@method不足)
・resourceルートのURLを手打ちして、想定と違うアクションに当たっている
・web.php と api.php を取り違えている(APIをブラウザGETで叩く等)
・ルートキャッシュが古く、定義変更が反映されていない

まず確認:実際に送っているメソッドとURLを一致させる

最初に見るべきは「URL」と「HTTPメソッド」。ブラウザのDevTools(Network)で Request Method と Request URL を確認する。
CLIなら curl で再現すると切り分けが早い。

# 例:GETで叩いているか
curl -i https://example.com/posts

# 例:POSTで叩く
curl -i -X POST https://example.com/posts

Laravel側では、例外メッセージに「Supported methods: …」が出るので、許容メソッドを確認できる。

原因1:ルート定義のメソッドと、リクエストメソッドが単純に違う

例:ルートはPOSTしかないのに、ブラウザでURLを開いてGETしている。

// routes/web.php
Route::post('/posts', [PostController::class, 'store']);

ブラウザで /posts を開くとGETになるため落ちる。
対処:
・一覧ページなら GET /posts を用意する
・登録は POST /posts をフォーム送信で呼ぶ

// OK
Route::get('/posts', [PostController::class, 'index']);
Route::post('/posts', [PostController::class, 'store']);

原因2:フォーム送信でPUT/PATCH/DELETEを送れていない(method spoofing不足)

HTMLフォームはGET/POSTしか送れないため、PUT/PATCH/DELETEはLaravelのmethod spoofingを使う。
発生条件:
・ルートは PUT /posts/{post} なのに、フォームがただのPOST

// routes/web.php
Route::put('/posts/{post}', [PostController::class, 'update']);

Bladeフォーム例(OK)。

<form method="POST" action="/posts/{{ $post->id }}">
  @csrf
  @method('PUT')
  <input type="text" name="title" value="{{ $post->title }}">
  <button type="submit">Update</button>
</form>

DELETEも同様。

<form method="POST" action="/posts/{{ $post->id }}">
  @csrf
  @method('DELETE')
  <button type="submit">Delete</button>
</form>

原因3:resourceルートで“どのURLがどのメソッドか”を勘違いしている

Route::resource を使うと、同じ /posts にGETとPOSTが共存し、/posts/{post} にGET/PUT/DELETEが割り当てられる。URL手打ちやリンク先ミスで想定と違うメソッドになることがある。

// routes/web.php
Route::resource('posts', PostController::class);

対処:
・ルート一覧で確認する

php artisan route:list

・Bladeのリンク/フォームは route() ヘルパを使ってURLを固定する

<a href="{{ route('posts.index') }}">一覧</a>

<form method="POST" action="{{ route('posts.store') }}">
  @csrf
  <button type="submit">作成</button>
</form>

原因4:web.php と api.php の取り違え(APIをブラウザで開く、CSRFや認証で混乱)

api.php のルートは通常 /api プレフィックスが付く。
発生条件:
・/api/posts はPOST前提なのに、ブラウザで開いてGETしている
・逆にweb側URLをAPIクライアントがPOSTしているが、web側はGETしかない
対処:
・APIはAPIクライアント/JSから正しいメソッドで叩く
・WebはGET画面を用意し、POSTはフォーム送信に限定する
curlでメソッドを明示して動作確認すると混乱が減る。

curl -i -X POST https://example.com/api/posts -H "Accept: application/json"

原因5:ルートキャッシュが古い(変更が反映されない)

本番で route:cache を使っていると、routesファイルを書き換えてもキャッシュを更新するまで反映されない。
発生条件:
・ルート定義をGET→POSTに変えたのに、まだ旧定義で動いている
対処:デプロイ手順でルートキャッシュを更新する。

php artisan route:clear
php artisan route:cache

同様に config:cache を使っている場合は、環境差切り分けで config も確認対象になる。

サンプル:GET画面+POST送信+PUT更新を揃える(ルートとBlade)

<!-- index.blade.php(作成フォーム) -->
<form method="POST" action="{{ route('posts.store') }}">
  @csrf
  <input name="title" />
  <button type="submit">Create</button>
</form>

<!-- updateフォーム(PUT) -->
<form method="POST" action="{{ route('posts.update', $post) }}">
  @csrf
  @method('PUT')
  <input name="title" value="{{ $post->title }}" />
  <button type="submit">Update</button>
</form>
[/code]

<h3>チェックリスト(上から順に確認する)</h3>

チェックリスト(上から順に確認する)

1) 実際に送っている Request Method と URL を確認したか(DevTools / curl)
2) 例外メッセージの「Supported methods: …」とルート定義が一致しているか
3) PUT/PATCH/DELETE のフォームに @method が入っているか(@csrf も)
4) resourceルートなら php artisan route:list で想定のルート名/URL/メソッドを確認したか
5) web/api のどちらのルートを叩いているか(/api プレフィックス含む)を確認したか
6) 本番で route:cache を使っているなら route:clear/route:cache を更新したか