Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Charkhan's avatar

[L5] MethodNotAllowedHttpException on a existing route.

I'm developping a public API since a couple of month under Laravel 5 and ran into a odd issue : All the methods PATCH, PUT and DELETE aren't working.

My local env : PHP 5.6 (also tried with 5.5), Nginx 1.6.2, and latest L5 commit (https://github.com/laravel/framework/commit/7e0a6472e7cfed16d3635b0da58d14f14a42c2a3).

I'm using Postman extention to test my API and set the requests like this :

POST /v1/feeds/ HTTP/1.1
Host: api.metanews.local
Cookie: laravel_session=eyJpdiI6Iit2M283bnQxcmRcL0F1TjNPa2g3OFwvQT09IiwidmFsdWUiOiJDbUQ0czJkTnV0OXBZNFQrNlN6S1Q4OGZKUlBWVERBRXE3MHhGa1ZDYUVWOG1VV1JMVUFGTEE0ZEpZR0tSOXk4UFl0b1lKZUlIME1RdWFmenBwRkdJQT09IiwibWFjIjoiNTVlYWQ3NjdhMTc4N2Y0MDE3YzY4Y2VkZDdjMmYzMGRhMzE4MDY2ODk3NjcxMWM3Zjg0NjAwOTk0Zjk1Mjc4YSJ9
X-Tenant-Key: stringexemple
Authorization: Basic stringbase64encoded
Cache-Control: no-cache
Postman-Token: fe87f096-09cd-d4c2-6e3d-eb6f1ccb7790
Content-Type: application/x-www-form-urlencoded

_method=PATCH

I also tried with curl, and had the same issue...

This kind of requests used to work. I didn't change my router.php since and nothing has change in the laravel's Router layer at my knowledge....

Routes are well-listed with the php artisan route:list (and I cleared any route cache just to be sure) but HTTP requests != (GET|POST) keeps failing.

My sample routes.php

<?php

$router->group(['prefix' => 'v1', 'namespace' => 'V1', 'middleware' => ['tenant', 'auth.basic', 'authorize']], function($router) {
    $router->get('feeds/trashed', ['uses' => 'FeedsController@trashed', 'as' => 'v1.feeds.trashed']);
    $router->patch('feeds/restore/{feeds}', ['uses' => 'FeedsController@restore', 'as' => 'v1.feeds.restore']);
    $router->resource('feeds', 'FeedsController', [
        'only' => ['index', 'show', 'store', 'update', 'destroy']
    ]);
});

php artisan route:list output

| Method      | URI                               | Name                  | Action                                                                             |
| ----------------- | ------------------------------------ | -------------------------- | -------------------------------------------------------------------------------------- |
| GET|HEAD | v1/feeds/trashed            | v1.feeds.trashed  | Metanews\Http\Controllers\V1\FeedsController@trashed |
| PATCH       | v1/feeds/restore/{feeds} | v1.feeds.restore  | Metanews\Http\Controllers\V1\FeedsController@restore  |
| GET|HEAD | v1/feeds                         | v1.feeds.index    | Metanews\Http\Controllers\V1\FeedsController@index     |
| POST         | v1/feeds                         | v1.feeds.store     | Metanews\Http\Controllers\V1\FeedsController@store     |
| GET|HEAD | v1/feeds/{feeds}              | v1.feeds.show    | Metanews\Http\Controllers\V1\FeedsController@show     |
| PUT           | v1/feeds/{feeds}              | v1.feeds.update  | Metanews\Http\Controllers\V1\FeedsController@update  |
| PATCH      | v1/feeds/{feeds}               | v1.feeds.update  | Metanews\Http\Controllers\V1\FeedsController@update  |
| DELETE     | v1/feeds/{feeds}              | v1.feeds.destroy  | Metanews\Http\Controllers\V1\FeedsController@destroy |

And debugger tells me :

MethodNotAllowedHttpException in RouteCollection.php line 207:

in RouteCollection.php line 207
at RouteCollection->methodNotAllowed(array('GET', 'HEAD', 'POST')) in RouteCollection.php line 194
at RouteCollection->getRouteForMethods(object(Request), array('GET', 'HEAD', 'POST')) in RouteCollection.php line 142
at RouteCollection->match(object(Request)) in Router.php line 702
at Router->findRoute(object(Request)) in Router.php line 628
at Router->dispatchToRoute(object(Request)) in Router.php line 604
at Router->dispatch(object(Request)) in Kernel.php line 155
at Kernel->Illuminate\Foundation\Http\{closure}(object(Request))
at call_user_func(object(Closure), object(Request)) in Pipeline.php line 141
at Pipeline->Illuminate\Pipeline\{closure}(object(Request)) in ShareErrorsFromSession.php line 55
at ShareErrorsFromSession->handle(object(Request), object(Closure)) in Pipeline.php line 125
at Pipeline->Illuminate\Pipeline\{closure}(object(Request)) in StartSession.php line 53
at StartSession->handle(object(Request), object(Closure)) in Pipeline.php line 125
at Pipeline->Illuminate\Pipeline\{closure}(object(Request)) in AddQueuedCookiesToResponse.php line 36
at AddQueuedCookiesToResponse->handle(object(Request), object(Closure)) in Pipeline.php line 125
at Pipeline->Illuminate\Pipeline\{closure}(object(Request)) in EncryptCookies.php line 40
at EncryptCookies->handle(object(Request), object(Closure)) in Pipeline.php line 125
at Pipeline->Illuminate\Pipeline\{closure}(object(Request)) in CheckForMaintenanceMode.php line 42
at CheckForMaintenanceMode->handle(object(Request), object(Closure)) in Pipeline.php line 125
at Pipeline->Illuminate\Pipeline\{closure}(object(Request))
at call_user_func(object(Closure), object(Request)) in Pipeline.php line 101
at Pipeline->then(object(Closure)) in Kernel.php line 108
at Kernel->sendRequestThroughRouter(object(Request)) in Kernel.php line 83
at Kernel->handle(object(Request)) in index.php line 53

After some research, it seems that the check function in \Illuminate\Routing\RouteCollection has an odd behavior

<?php
/**
     * Determine if a route in the array matches the request.
     *
     * @param  array  $routes
     * @param  \Illuminate\http\Request  $request
     * @param  bool  $includingMethod
     * @return \Illuminate\Routing\Route|null
     */
    protected function check(array $routes, $request, $includingMethod = true)
    {
        return array_first($routes, function($key, $value) use ($request, $includingMethod)
        {
            return $value->matches($request, $includingMethod);
        });
    }

The $value->matches returns true if a declared route is matched, and with some dd(), I discovered that it returns true at some point, but the check function returns null...

0 likes
4 replies
Limweb's avatar

comment Kernel.php / VerifyCsrfToken and test PUT DELETE POST again

Charkhan's avatar

Thanks for reading,

But it's already commented.... :( I posted the issue on github.

heyalda's avatar

I had this issue in the following scenario...

I was posting a form to an endpoint /v1/message/send from a form that is loaded via getting the url /message/edit. The send message is also used by a mobile client, hence the version number in the URI.

// The routes
Route::get('message/edit', 'MessageController@editMessage')->name('message.edit');
Route::post('v1/message/send', 'MessageController@sendMessage')->name('message.send');

The problem was that in the MessageController@sendMessage method I was returning the view a the end of the method.

        return view('messageSender');

The problem with this is that the post to v1/message/send changed the URI to v1/message/send. Then if I tried to submit the form and the form had validation errors, the validation would try to GET v1/message/send and that is not a valid route.

To fix the issue, I changed the return value at the end of MessageController@sendMessage to the following and it of course no longer had the MethodNotAllowedHttpException error because the scenario where a GET request could accidentally be called by Laravel input validation on a POST only route could no longer occur.

        return redirect()->route('message.edit');

Please or to participate in this conversation.