In Laravel, which HTTP verbs do Route::get, Route::match and Route::any register, and how do you confirm them?
answer
- one router method per verb
- get also answers HEAD
- match takes a list of verbs
- any: all seven router verbs
- route:list shows GET|HEAD, -v adds middleware
basics
~20 sRoute::get registers GET and HEAD; post, put, patch, delete and options register one verb each; Route::match registers the verbs you list (plus HEAD when GET is listed); Route::any registers all seven router verbs. php artisan route:list shows each route's verbs, URI, name and action.
solid answer
~40 sLaravel's router has one method per verb: `Route::get`, `post`, `put`, `patch`, `delete` and `options`. `Route::get` is special: it registers **GET and HEAD**, so HEAD requests work for free. `Route::match(['get', 'post'], '/search', ...)` registers the verbs you list (Laravel adds HEAD to any route that has GET), and `Route::any` registers all seven verbs the router knows (GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS). A request whose URI matches but whose verb does not gets a 405 Method Not Allowed. To confirm what you registered, run `php artisan route:list`: it prints `GET|HEAD`, `POST` or `ANY` per route, `-v` adds middleware, and `--method=`, `--path=` and `--name=` filter the list.
code
bash · 7 linesphp artisan route:list --path=recipes
# with middleware per route
php artisan route:list --path=recipes -v
# only POST routes, in registration order
php artisan route:list --method=POST --sort=definitiongo deeper
Know the verb methods, that Route::get also registers HEAD, and that route:list shows what you registered.
Explain match versus any, why a wrong verb produces 405 rather than 404, and which route:list options filter and expand the output.
Push back on any() in reviews, keep one route per verb and URI, and use route:list --json or --method in CI to catch accidental write routes.
Set conventions for verb use across teams so APIs stay predictable, and decide where route inventories are checked automatically.
## One method per verb Laravel's router (the `Route` facade resolves `Illuminate\Routing\Router`) exposes a registration method for each HTTP verb. Every one takes a URI and an **action**: a closure, a `[Controller::class, 'method']` array, or an invokable controller class. | Call | Verbs registered | Typical use | |---|---|---| | `Route::get($uri, $action)` | GET, HEAD | pages, reads | | `Route::post(...)` | POST | form submissions, creates | | `Route::put(...)` / `Route::patch(...)` | PUT / PATCH | full / partial updates | | `Route::delete(...)` | DELETE | removals | | `Route::options(...)` | OPTIONS | rarely written by hand | | `Route::match(['get', 'post'], ...)` | the listed verbs, plus HEAD when GET is listed | a search form that also accepts a GET link | | `Route::any(...)` | GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS | proxies, catch-all handlers | The seven verbs in the last row are the router's static `Router::$verbs` list; `Route::any` registers exactly that list. `Route::match` upper-cases whatever you pass, so `['get', 'post']` and `['GET', 'POST']` are equivalent. The `Route` constructor also appends **HEAD** to any route whose verbs include GET, whichever registration method created it. ## Why GET also answers HEAD A **HEAD** request asks for the headers a GET would return, without the body. Monitoring tools and some crawlers use it. Because `Route::get` registers both verbs (and the `Route` constructor adds HEAD to any GET route anyway), a recipe page defined with `Route::get('/recipes/{slug}', ...)` answers HEAD without extra code, and `route:list` shows the route as `GET|HEAD`. ## What happens when the verb does not match When the path matches a route but no route for the request's verb exists, Laravel does not return 404. It throws a **`MethodNotAllowedHttpException`** (405) with a message such as `The POST method is not supported for route recipes. Supported methods: GET, HEAD.` Two details follow from the router's source: - An **OPTIONS** request to a URI that has routes for other verbs gets an automatic `200` response whose `Allow` header lists those verbs, even though you never registered an OPTIONS route. - The 405 message is the fastest clue that a form or client is using the wrong verb (see method spoofing for HTML forms). ## Prefer explicit verbs over any and match - **`Route::any` widens the attack surface.** A read-only page registered with `any` also accepts POST, PUT and DELETE, so writes can land on a handler that never expected them. - **Overlapping definitions are ambiguous.** The router keys routes by verb and URI, so `Route::get('/menu', A)` plus `Route::any('/menu', B)` leaves two definitions competing for GET. Which one wins depends on registration details, so keep one route per verb and URI. - **`Route::match` is the honest middle ground** when one handler genuinely serves two verbs, such as a search endpoint reachable by a GET link and a POST form. ## Confirming the result with route:list `php artisan route:list` prints every registered route. Useful options, all defined in the framework's `RouteListCommand`: 1. `-v` adds each route's middleware; `-vv` expands middleware groups into their members. 2. `--method=POST`, `--path=api`, `--name=recipes` and `--domain=` filter the list. 3. `--except-vendor` hides package routes; `--only-vendor` shows only them. 4. `--sort=definition` lists routes in registration order instead of the default sort by URI; `-r` reverses. 5. `--json` prints machine-readable output for scripts and CI checks. In the method column, a route registered with `Route::any` (or `Route::redirect`, which uses `any` internally) is shown as `ANY`. ## Reading a 405 in an interview Interviewers like to paste the error text and ask what went wrong. The message names the verb the router saw and the verbs it would have accepted, so it answers most questions on its own: - **POST not supported, supported GET, HEAD**: a form or client is posting to a read-only URL; either the route should be `Route::post` or the client is wrong. - **GET not supported, supported POST**: someone opened a form-handling URL in the browser address bar, or a plain link points at it. - **POST not supported, supported PUT, PATCH or DELETE**: an HTML form is missing its method-spoofing field. In every case `route:list --path=...` confirms which verbs the URI really has. ## A worked example on a recipe site ```php use App\Http\Controllers\RecipeController; use Illuminate\Support\Facades\Route; Route::get('/recipes', [RecipeController::class, 'index']); Route::post('/recipes', [RecipeController::class, 'store']); Route::match(['get', 'post'], '/recipes/search', [RecipeController::class, 'search']); ``` Running `php artisan route:list --path=recipes` shows `GET|HEAD recipes`, `POST recipes` and `GET|POST|HEAD recipes/search` (HEAD is appended after the verbs you listed). A PATCH to `/recipes` gets a 405, not a 404, because the URI exists for other verbs.
- What does a Laravel app return for an OPTIONS request to a URI that only has GET and POST routes?The router finds routes for other verbs on that URI and, because the request is OPTIONS, answers with a `200` and an `Allow` header listing them (for example `GET,HEAD,POST`). For any other unmatched verb it throws `MethodNotAllowedHttpException`, a 405. CORS preflight handling is a separate concern of the CORS middleware.
- Why is Route::any a poor default for a page that only reads data?It registers all seven verbs, so the same handler also accepts POST, PUT, PATCH and DELETE. Clients that send the wrong verb get a normal response instead of a 405 that would expose their bug, and a read-only handler may end up processing writes it was never designed to validate.
saying these in an interview costs you the question
- Route::get answers only GET, so HEAD requests fail
- Route::any is a clean way to serve a form and its submission
- A wrong verb on an existing URI returns 404
- route:list shows each route's middleware without extra flags
- Two routes for the same verb and URI both run in order