Introduction
Laravel 13 was released in March 2026. This guide walks through upgrading from Laravel 12.x to 13.x.Estimated time to upgrade is about 10 minutes. Actual impact from breaking changes depends on your app’s size and features you use.
AI-assisted upgrade
You can also automate the upgrade using Laravel Boost. Boost is a first-party MCP server that provides staged upgrade prompts to AI assistants. After installing it into a Laravel 12 application, you can start the upgrade to Laravel 13 by using the/upgrade-laravel-v13 slash command in Claude Code, Cursor, OpenCode, Gemini, or VS Code. This command requires laravel/boost ^2.0.
For AI tools that don’t support slash commands, you can execute the same upgrade steps by referring to the prompt file directly. Paste the following prompt to your AI as-is.
prompt
Changes by impact level
Impact: high
- Dependency updates
- Laravel installer update
- Request forgery protection (CSRF)
Impact: medium
- Cache
serializable_classessetting - Session
serializationsetting
Impact: low
- Cache prefixes and session cookie name
- Collection model serialization
Container::calland nullable class defaults- Domain route registration priority
JobAttemptedevent exception payload- Manager
extendcallback binding - MySQL
DELETEqueries (JOIN / ORDER BY / LIMIT) - Pagination Bootstrap view names
- Polymorphic pivot table name generation
QueueBusyevent property rename- Cross-test
Strfactory reset
Upgrade steps
Update dependencies
Impact: high Update the following dependencies incomposer.json.
Update the Laravel installer
Impact: high If you use the Laravel installer CLI to create new Laravel apps, update it to the version that supports Laravel 13.x. If installed viacomposer global require:
Breaking changes
Security
Request forgery protection
Impact: high Laravel’s CSRF middleware has been renamed fromVerifyCsrfToken to PreventRequestForgery. Origin validation via the Sec-Fetch-Site header has also been added.
VerifyCsrfToken and ValidateCsrfToken remain as deprecated aliases, but any direct references should be updated to PreventRequestForgery. Be especially careful when excluding the middleware in tests or route definitions.
preventRequestForgery(...) is also now available in the middleware configuration API.
Cache
Cache prefixes and session cookie name
Impact: low Laravel’s default cache and Redis key prefixes now use hyphen-separated suffixes. The default session cookie name now usesStr::snake(...).
Most apps set these explicitly in config, so they aren’t affected. Only apps that depend on the framework’s fallback settings are affected.
.env.
Cache serializable_classes setting
Impact: medium
A serializable_classes option has been added to the default cache configuration, and the default is false. This mitigates PHP deserialization gadget chain attacks if APP_KEY is leaked.
If your app intentionally stores PHP objects in the cache, you must explicitly list the classes allowed for deserialization.
Session serialization setting
Impact: medium
The Laravel 13 skeleton (laravel/laravel) adds 'serialization' => 'json' in config/session.php. However, the internal framework default remains php.
The official upgrade guide doesn’t mention this setting change. This likely means you don’t have to change it. Laravel occasionally has changes that aren’t documented in the upgrade guide because upgrading from the immediately preceding version isn’t affected. When someone tries to upgrade years later without information, they get stuck—so unofficial records matter.
To keep the same behavior as Laravel 12 and earlier, set 'serialization' => 'php' explicitly.
Container
Container::call and nullable class defaults
Impact: low
Container::call now respects the default value of a nullable class parameter when no binding exists (matching the behavior introduced for constructor injection in Laravel 12).
Database
MySQL DELETE queries
Impact: low
Laravel now compiles a full DELETE ... JOIN query including ORDER BY and LIMIT for MySQL grammar.
In previous versions, ORDER BY / LIMIT clauses could be ignored on DELETEs with JOINs. In Laravel 13, those clauses are included in the generated SQL. As a result, some database engines that don’t support this syntax may throw a QueryException.
Eloquent
Polymorphic pivot table name generation
Impact: low When inferring the table name of polymorphic pivot models that use a custom pivot model class, Laravel now generates a plural name. If you relied on the previous singular inferred name, define the table name explicitly on the pivot model.Collection model serialization
Impact: low When Eloquent model collections are serialized and restored (e.g. in queued jobs), eager-loaded relationships are now restored for the models. If you had code that relied on relationships being absent after deserialization, fixes are required.Queue
JobAttempted event exception payload
Impact: low
The Illuminate\Queue\Events\JobAttempted event now exposes the exception object (or null) via $exception, in place of the previous boolean $exceptionOccurred property.
QueueBusy event property rename
Impact: low
The Illuminate\Queue\Events\QueueBusy event property $connection has been renamed to $connectionName for consistency with other queue events.
Routing
Domain route registration priority
Impact: low Routes with an explicit domain now take priority over non-domain routes in route matching. This ensures catch-all subdomain routes behave consistently even when non-domain routes are registered first.Support
Manager extend callback binding
Impact: low
Custom driver closures registered via a Manager’s extend method are now bound to the manager instance.
If these callbacks previously referenced another object (like a service provider instance) as $this, you’ll need to move the value into the closure capture with use (...).
Cross-test Str factory reset
Impact: low
Laravel now resets custom Str factories during test tear-down.
If you relied on custom UUID / ULID / random string factories persisting across test methods, set them in each relevant test or a setup hook.
Views
Pagination Bootstrap view names
Impact: low The internal pagination view names for the Bootstrap 3 defaults are now explicit.Deprecated features
| Feature | Replacement |
|---|---|
VerifyCsrfToken middleware | PreventRequestForgery |
ValidateCsrfToken middleware | PreventRequestForgery |
JobAttempted::$exceptionOccurred | JobAttempted::$exception |
QueueBusy::$connection | QueueBusy::$connectionName |
Added contract methods
Impact: very low Only affects you if you have custom implementations.Dispatcher contract
The Illuminate\Contracts\Bus\Dispatcher contract adds dispatchAfterResponse($command, $handler = null).
ResponseFactory contract
The Illuminate\Contracts\Routing\ResponseFactory contract adds an eventStream signature.
MustVerifyEmail contract
The Illuminate\Contracts\Auth\MustVerifyEmail contract adds markEmailAsUnverified().
Queue contract
The Illuminate\Contracts\Queue\Queue contract adds the following queue-size inspection methods (previously declared only in docblocks).
pendingSizedelayedSizereservedSizecreationTimeOfOldestPendingJob
Store / Repository contract
A touch method has been added to the cache contracts for extending TTLs.
New feature highlights
AI-assisted upgrade (Laravel Boost)
Laravel Boost is the official MCP server. It integrates with AI editors so you can semi-automate the upgrade with the/upgrade-laravel-v13 command.
Origin validation via Sec-Fetch-Site
The PreventRequestForgery middleware performs additional origin validation using the Sec-Fetch-Site header, strengthening CSRF protection.
Safe cache deserialization
Withserializable_classes configuration, only allowed classes are deserialized. Security against PHP deserialization attacks is improved.
SSE (Server-Sent Events) eventStream
eventStream has been added to the ResponseFactory contract, improving Server-Sent Events support.
Queue visibility improvements
Methods on theQueue contract like pendingSize, delayedSize, and reservedSize let you monitor queue state more granularly.
Common migration issues and fixes
Issue: CSRF-related tests fail
Symptom: Tests referencingVerifyCsrfToken fail with a class-not-found error.
Fix: Update all references to PreventRequestForgery.
Issue: Objects can’t be restored from the cache
Symptom: Data fetched from the cache isnull, or an UnserializationFailedException is thrown.
Fix: Add the classes you use to serializable_classes in config/cache.php, or convert cached values to arrays.
Issue: JobAttempted listeners break
Symptom: $event->exceptionOccurred is null or undefined.
Fix: Change to $event->exception !== null.
Issue: Sessions are invalidated
Symptom: Users are logged out after the upgrade. Fix: The default session cookie name has changed. Explicitly setSESSION_COOKIE in .env to keep the previous value.
Issue: Cache keys can’t be found
Symptom: Cache misses increase after the upgrade. Fix: The cache prefix has changed. SetCACHE_PREFIX in .env or clear the cache.
References
- Official upgrade guide (English)
- laravel/laravel diff (12.x → 13.x)
- Laravel Shift — community service to automate upgrades
- Laravel Boost — MCP server for AI-assisted upgrades