If you see a MethodNotAllowedHttpException when accessing routes or submitting forms in Laravel, here's why that happens and how to fix it.
When we define routes in Laravel, we use the Route
facade to specify which HTTP method it should respond to. Here's an example:
Route::get('/users', UserController::class)->name('users');
This route would be accessible as GET request (you visit it in the browser).
Here's an addition to this, where we're also able to POST through to a /users
endpoint to store a user:
Route::get('/users', UserController::class)->name('users');
Route::post('/users', UserStoreController::class)->name('users.store');
Because both of these routes share the same path (/users
), we're able to either visit it in the browser or POST through with a form, like this:
<form action="{{ route('users.store') }}" method="post">
@csrf
<!-- Your form fields here -->
<button type="submit">Create user</button>
</form>
A MethodNotAllowedHttpException
exception is thrown in Laravel when you attempt to send an HTTP request to a route, and the method hasn't explicitly been defined.
Here's an example:
Route::post('/topics', TopicStoreController::class)->name('topics.store');
Imagine this is the only route with the /topics
path. By attempting to visit /topics
in your browser, a MethodNotAllowedHttpException
will be thrown because no GET route exists for this.
The opposite is true for any HTTP method.
A common reason for the MethodNotAllowedHttpException
exception being thrown is that you've defined a route where you mean to create a resource but use the GET HTTP method.
Here's an example:
Route::get('/users', UserStoreController::class)->name('users.store');
This route is intended to create a user, but by using the GET method, it won't match a POST request from a form. Therefore, you'll see a MethodNotAllowedHttpException
!
Another common reason for seeing the MethodNotAllowedHttpException
error is not using the correct method type when building forms.
Let's take our /users
example route with the correct method:
Route::post('/users', UserStoreController::class)->name('users.store');
Suppose we were to build a form like this (without explicitly setting the method
attribute on the form). In that case, we'd see a MethodNotAllowedHttpException
error:
<form action="{{ route('users.store') }}">
@csrf
<!-- Your form fields here -->
<button type="submit">Create user</button>
</form>
Another reason you may see the MethodNotAllowedHttpException
error in Laravel is when working with PUT/PATCH/DELETE requests.
Let's say we have a route that deletes a user:
Route::delete('/users', UserDestroyController::class)->name('users.destroy');
Applying this to a form might look like this:
<form action="{{ route('users.destroy', $user) }}" method="post">
@csrf
<button type="submit">Delete this user</button>
</form>
This would result in a MethodNotAllowedHttpException
exception because we're using POST, and the route is defined under DELETE.
No worries, though, you can spoof the HTTP method in Laravel, like this:
<form action="{{ route('users.destroy', $user) }}" method="post">
@csrf
@method('DELETE')
<button type="submit">Delete this user</button>
</form>
Laravel will now know which method you're using and will find the correct route to send the request to.
Method spoofing is necessary because only GET and POST are supported in HTML forms. If you were building an API this wouldn't be a problem, but spoofing allows us to maintain a sensible set of HTTP methods for our resources.
When you're defining routes in Laravel, checking the few points we've covered in this article will help you avoid a MethodNotAllowedHttpException
exception being thrown.
It's a little more work than using POST everywhere, but by making use of all of the HTTP methods available, you'll end up with a much cleaner set of routes!