Route::post
Registers a route that answers POST requests, typically for creating a resource.
post(string $uri, array|string|callable $action = null): Illuminate\Routing\RouteParameters
| Parameter | Type | Description |
|---|---|---|
$$uri | string | The URI pattern to match, with any route parameters wrapped in braces. |
$$actionoptional | array|string|callable= null | A 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.phpmust include@csrf. Without it the request is rejected with a 419 before your controller runs. Routes inroutes/api.phpare stateless and are not in thewebmiddleware 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__invokemethod; no method name is needed. - Ordering. As with every verb, register POST routes before any
match,anyorredirectroute that shares the URI.
Example
<?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/serverWhen 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.
<?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/photosA 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.
<?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