Laravel upgrades

Upgrading from Laravel 12 to 13

Laravel 13 needs PHP 8.3 or later and few code changes. Most of the risk sits in four areas: CSRF references, cached objects, sessions and upserts.

By itmarkerz Technologies. Updated 30 October 2026.

Before you start

Laravel 12 stopped receiving bug fixes on August 13, 2026 and gets security fixes until February 24, 2027. Laravel 13, released on March 17, 2026, has security fixes until March 17, 2028. This guide follows the official upgrade guide, read on 30 October 2026, and picks out what we would check first.

  1. Confirm PHP 8.3 or later on every server, CI runner and worker, not only the web servers.
  2. Run composer why-not laravel/framework ^13.0 to find packages that aren’t ready.
  3. Work from a branch with green tests. Use our upgrade checklist for the steps around the code.

Step 1: update dependencies

Update these in composer.json, where you have them installed, then run composer update. PHPUnit 12 and Pest 4 may need changes in the test suite, so budget for those separately from the framework itself.

"require": {
  "laravel/framework": "^13.0",
  "laravel/tinker": "^3.0"
},
"require-dev": {
  "laravel/boost": "^2.0",
  "phpunit/phpunit": "^12.0",
  "pestphp/pest": "^4.0"
}

Step 2: request forgery middleware

The CSRF middleware is now PreventRequestForgery and also checks the request origin using the Sec-Fetch-Site header. VerifyCsrfToken and ValidateCsrfToken still work as deprecated aliases, so nothing breaks at once. Search your routes and tests for the old names and update them.

use Illuminate\Foundation\Http\Middleware\PreventRequestForgery;

// Laravel 12: ->withoutMiddleware([VerifyCsrfToken::class]);
->withoutMiddleware([PreventRequestForgery::class]);

Step 3: check what you cache

The default cache config gains a serializable_classes option set to false. It hardens unserialization against gadget-chain attacks if APP_KEY leaks. If your application puts PHP objects in the cache and you adopt this setting, those reads stop returning the objects you expect, which a light test can miss when the cache is empty. List the classes, or move to arrays.

// config/cache.php
'serializable_classes' => [
    App\Data\CachedDashboardStats::class,
    App\Support\CachedPricingSnapshot::class,
],

Step 4: decide about sessions

The new application skeleton sets session serialization to json. If you sync your config files with the skeleton, changing an existing application from php to json signs out every active user. Keep php for a quiet release. Move to json when nothing in your session is a PHP object and you can accept the sign-outs.

Step 5: the database changes

  • upsert on MySQL and MariaDB now throws InvalidArgumentException when uniqueBy is empty, even though those drivers ignore its value. Search for these calls, especially in imports.
  • DELETE queries with JOIN, ORDER BY and LIMIT now include all those clauses. MySQL, and MariaDB before 11.8.1, may throw a QueryException where the extra clauses used to be ignored.
  • Custom pivot models on polymorphic relations now get pluralised inferred table names. Set the table explicitly if you relied on the singular form.

Step 6: the smaller changes worth a search

  • Queue listeners: JobAttempted now exposes $exception in place of $exceptionOccurred, and QueueBusy renames $connection to $connectionName.
  • Cache and session names: if you rely on framework defaults, prefixes change to hyphens, which empties the cache and resets the session cookie. Set CACHE_PREFIX, REDIS_PREFIX and SESSION_COOKIE to keep the old values.
  • Global helpers: a new PHP 8.5 polyfill defines array_first() and array_last(), which can clash with old helper packages. Prefer Arr::first().
  • Routing: routes with an explicit domain now match before routes without one, which matters if you use catch-all subdomains.
  • Tests: custom Str factories reset between tests, and the default password reset subject is now “Reset your password”.

If you maintain custom cache stores, queue drivers or dispatcher implementations, the contracts gained new methods too. The official guide lists them.

Releasing it

Restart queue workers during the deploy (php artisan reload handles long-running services), watch failed jobs and sign-ins afterwards, and keep the previous release ready to restore. Coming from an older version? Start with our guides for Laravel 11 or Laravel 10.

Upgrades we have delivered

Questions

How long does the upgrade from Laravel 12 to 13 take?

Laravel's guide estimates 10 minutes, and its release notes call this a relatively minor upgrade. Testing and checking your packages take longer than the code changes, so the real figure depends on your application.

Which PHP version does Laravel 13 need?

PHP 8.3 or later. Laravel lists PHP 8.3–8.5 as supported for version 13.

Will upgrading log my users out?

Only if you change the session serialization setting from php to json, which invalidates active sessions. Keep it at php to avoid that, or switch deliberately and accept that users must sign in again.

Should I use Laravel Boost for the upgrade?

It is a reasonable start. Boost 2 adds an /upgrade-laravel-v13 command for AI coding assistants. Review its changes as you would any other pull request, and run your own checks on cache, sessions and queues.

Want this done for you?

If your application carries billing, ordering or customer data, we can run the upgrade and hand it back with a fixed price agreed up front.

We reply within one business day (IST).