Route::post

Registers a route that answers POST requests, typically for creating a resource.

Signature
post(string $uri, array|string|callable $action = null): Illuminate\Routing\Route

Parameters

ParameterTypeDescription
$$uristringThe URI pattern to match, with any route parameters wrapped in braces.
$$actionoptionalarray|string|callable= nullA closure, a [Controller::class, 'method'] array, an invokable controller class name, or a 'Controller@method' string.

Return value

Illuminate\Routing\RouteThe registered Route instance, so name(), middleware(), where() and friends can be chained.

Errors

Nothing at registration. At request time a POST route in routes/web.php rejects a request with a missing or stale CSRF token, producing a 419 response.

Route::post registers the write half of a URI pair: GET /photos lists, POST /photos creates. It is the verb behind a resource controller's store action and behind virtually every HTML form that is not a search box.

When to reach for it

Creating a record, triggering a job, accepting a webhook, logging a user in. POST is also the right choice for any non-idempotent action that does not map cleanly onto PUT or DELETE. If the whole CRUD set is needed, Route::resource registers this route for you.

What trips people up

  • CSRF. Any HTML form targeting a POST route in routes/web.php must include @csrf. Without it the request is rejected with a 419 before your controller runs. Routes in routes/api.php are stateless and are not in the web middleware group, so they are not CSRF-protected.
  • Redirect after POST. Return a redirect rather than a view, or a browser refresh re-submits the form.
  • Invokable controllers. Passing a bare class name — Route::post('/server', ProvisionServer::class) — routes to that class's __invoke method; no method name is needed.
  • Ordering. As with every verb, register POST routes before any match, any or redirect route that shares the URI.

Example

Example · php
<?php

use App\Http\Controllers\PhotoController;
use App\Http\Controllers\ProvisionServer;
use Illuminate\Support\Facades\Route;

Route::post('/photos', [PhotoController::class, 'store'])
    ->middleware('auth')
    ->name('photos.store');

// A bare class name routes to that controller's __invoke method.
Route::post('/server', ProvisionServer::class)->name('server.provision');

echo route('photos.store');
echo PHP_EOL;
echo route('server.provision');
// http://example.com/photos
// http://example.com/server

When to use it

  • Accept a contact form submission and redirect back with a status message.
  • Create a record from a validated form request in a store controller method.
  • Receive a third-party webhook on a stateless api.php route.
  • Route a one-off action such as provisioning to a single-action __invoke controller.

Frequently asked questions

Why do I get a 419 Page Expired when posting a form?

The CSRF token was missing or stale. Add @csrf inside any form posting to a routes/web.php endpoint; a 419 also appears when the session expired while the form sat open.

Do API POST routes need a CSRF token?

No. Routes in routes/api.php are assigned the stateless api middleware group, which does not include CSRF verification. Authenticate them with a token guard such as auth:sanctum instead.

How do I point a POST route at a controller with no method name?

Pass the class itself — Route::post('/server', ProvisionServer::class) — and give that controller an __invoke method. Generate one with php artisan make:controller ProvisionServer --invokable.

More examples

The form, the token, and the controller

This is the full round trip: a GET route renders the form, the @csrf directive writes the hidden token the POST route requires, and the store method redirects instead of returning a view so a browser refresh does not repeat the submission. Dropping @csrf is what produces the familiar 419 Page Expired.

Example · php
<?php

// routes/web.php
use App\Http\Controllers\PhotoController;
use Illuminate\Support\Facades\Route;

Route::get('/photos/create', [PhotoController::class, 'create'])->name('photos.create');
Route::post('/photos', [PhotoController::class, 'store'])->name('photos.store');

// resources/views/photos/create.blade.php
// <form method="POST" action="{{ route('photos.store') }}">
//     @csrf
//     <input name="caption">
// </form>

// app/Http/Controllers/PhotoController.php
public function store(Request $request): RedirectResponse
{
    $data = $request->validate(['caption' => 'required|string|max:255']);

    $photo = Photo::create($data);

    return redirect()->route('photos.show', $photo);
}

echo route('photos.store');
// http://example.com/photos

A stateless API endpoint with rate limiting

The generated URL includes /api even though the route file never mentions it, because Laravel applies the apiPrefix from bootstrap/app.php. These routes sit in the api middleware group, so no CSRF token is expected; access is controlled with auth:sanctum and abuse with throttle:10,1 instead.

Example · php
<?php

// routes/api.php — the /api prefix is applied automatically.
use App\Http\Controllers\Api\TokenController;
use Illuminate\Support\Facades\Route;

Route::post('/tokens', [TokenController::class, 'store'])
    ->middleware('throttle:10,1')
    ->name('api.tokens.store');

Route::post('/photos', [PhotoController::class, 'store'])
    ->middleware('auth:sanctum')
    ->name('api.photos.store');

echo route('api.tokens.store');
// http://example.com/api/tokens

Discussion

  • Be the first to comment on this lesson.