Skip to main content
Laravel 13

Laravel 13 Landed — Here's What Actually Changed

Laravel 13 tested against a real production app: PHP 8.3 gate, attributes, Cache::touch(), and the quiet breaks — CSRF rename, cache prefixes, boot().

7 min
Read time
1,282
Words
Published
Last revised
Engr Mejba Ahmed

Written by

Engr Mejba Ahmed

Share Article

Laravel 13 Landed — Here's What Actually Changed

Laravel 13 shipped on March 17, 2026, and the headline is that almost nothing breaks. I do not fully buy the headline. I maintain this site (a production Laravel app with the legacy pre-11 directory structure) plus client applications at Ramlit, and when I ran the upgrade checklist against those codebases, the interesting findings were not the features. They were four or five quiet changes that produce no error message at all, just wrong behavior. This guide covers both halves: what you get, and what to audit before you trust a green deploy.

Laravel 13 Landed — Here's What Actually Changed - overview of the hard gate: php 8.3 minimum, the features that changed how my code reads

The hard gate: PHP 8.3 minimum

Laravel 13 drops PHP 8.2 entirely. Minimum is 8.3, supported through 8.5. On shared hosting this is usually a dropdown in the control panel; on a server you manage, it is the first thing to schedule, because nothing else in the upgrade matters until the runtime is there. Check your composer.json platform config and any CI matrix at the same time. If you are still on Laravel 10 or 11, note that you cannot skip majors: the path is 11 to 12 to 13, one upgrade guide at a time. Coming from 12, the dependency bumps are laravel/framework to ^13.0, laravel/tinker to ^3.0, and PHPUnit to ^12.0.

The features that changed how my code reads

PHP attributes across the framework

Laravel 13 brings first-class attribute support to models, jobs, commands, and more. Model configuration that used to be scattered across properties can now sit on the class declaration:

#[Table('blog_posts')]
#[Fillable(['title', 'content', 'published_at'])]
class Post extends Model
{
    // ...
}

It is optional and fully backward compatible, which is the right call. My take after trying it on a fresh service: use attributes for new code, do not run a big-bang conversion of existing models. The churn produces enormous diffs for zero behavior change, and mixed style within one model is worse than old style.

Cache::touch()

Small API, real savings. Extending a cached item's TTL used to mean fetching the value and re-storing it. Cache::touch('key', 3600) extends the TTL in place, works across drivers, and returns false if the key is missing. On this site I cache rendered post content per locale and bust it with model observers, a pattern I documented in my shared hosting optimization case study. touch() fits exactly the "content unchanged, keep it warm" path that previously forced a wasteful round trip.

The Laravel AI SDK goes first-party

The AI SDK shipped as a stable first-party package the same day as the framework: one API for text generation, tool-calling agents, embeddings, and vector stores. I had already explored the 0.3.0 pre-release in a real app, and the promotion to first-party changes the calculus: it is now a reasonable default for AI features in Laravel instead of a bet on an unproven package. Alongside it, 13 adds native vector search support and JSON:API resources, plus Queue::route() for centralizing job-to-queue mapping.

The quiet breaks I found auditing a real codebase

This is the section I wish someone had written for me. None of these throw during composer update.

CSRF middleware is renamed, and it does more now

VerifyCsrfToken is now PreventRequestForgery, and beyond the rename it adds origin verification using the browser's Sec-Fetch-Site header alongside token validation. The old name survives as a deprecated alias, so your app boots fine. Where it bites is tests: any test doing withoutMiddleware(VerifyCsrfToken::class) is now excluding an alias while the real middleware still runs.

This one is personal. Because this site kept the Laravel 10 directory structure, there is a literal app/Http/Middleware/VerifyCsrfToken.php in my repo with a $except array. Legacy-structure apps like mine have to move those exclusions to the new class, not just bump the framework version. If you have webhook endpoints excluded from CSRF, verify them first after upgrading.

Cache and session prefixes switched from underscores to hyphens

The default cache and session prefix format changed. If you never pinned CACHE_PREFIX and SESSION_PREFIX in .env, then the moment Laravel 13 hits production, every cache key misses and every active session dies. Users get logged out mid-request, and your database or Redis takes a cold-cache stampede at deploy time.

On a low-traffic blog that is a shrug. On a client app with thousands of live sessions it is an incident. The fix is one line per prefix in .env, set to the old values, before you deploy. This is the single highest-leverage line in the whole upgrade for anyone running real traffic.

Model boot() restriction

Instantiating a model while that same model is still booting now throws a LogicException. This mostly targets package authors, but I have seen the pattern in application code too: a boot() method that creates a default related record, or seeds a settings row, by newing up the model being booted. It used to work by accident. Now it fails loudly, which honestly is the better behavior, but you want to find those spots before production does. Grep your models' boot() and booted() methods for new static, ::create, and ::firstOrCreate on the same class.

DELETE with JOIN now respects ORDER BY and LIMIT

Previously, ORDER BY and LIMIT on a joined delete were silently ignored. Laravel 13 compiles and sends them to MySQL, which rejects that combination, so a query that "worked" for years can start throwing QueryException. If you have chunked cleanup jobs that join and limit, test them against 13 explicitly.

The smaller ones worth a grep

  • JobAttempted event: the boolean exceptionOccurred property became exception (a Throwable or null). Listeners checking the old boolean fail silently, since null is falsy.
  • QueueBusy event: $connection renamed to $connectionName.
  • Routes with explicit domains now match before routes without, which can reroute requests in multi-tenant subdomain apps.
  • Polymorphic pivot table name inference now pluralizes. Declare $table explicitly if you relied on singular names.
  • Cache serialization is hardened: serializable_classes defaults to false, so apps storing PHP objects in cache must allowlist classes or move to arrays.
  • MySQL upserts with an empty uniqueBy now throw instead of generating invalid SQL.

The upgrade order that worked for me

  1. Get PHP 8.3 live under your current Laravel version first, and soak it for a few days.
  2. Pin CACHE_PREFIX and SESSION_PREFIX in .env before touching composer.
  3. Bump the dependencies, run the suite, and fix the deprecation notices rather than silencing them.
  4. Grep for the quiet breaks above: withoutMiddleware, exceptionOccurred, boot() self-instantiation, joined deletes.
  5. Only then start adopting the new toys, attributes and Cache::touch() first since they are zero-risk.

If you are earlier in the version ladder, my Laravel 12 installation and setup guide and the 12.51 feature roundup cover the previous rung.

Where this leaves Laravel

The release tells you where the framework is going: fewer breaking changes per major, features shipped continuously across the year, and AI promoted to a first-party concern. For those of us running Laravel in production, that is the trade we want. The cost is that "zero breaking changes" marketing makes teams skip the audit, and the breaks that remain are exactly the silent kind this post lists. Support runs with bug fixes into Q3 2027 and security fixes into Q1 2028, so there is no need to rush, but there is also no reason to be the app that mass-logs-out its users over an unpinned session prefix.

Upgrading a production app and want a second pair of eyes?

I have run this checklist against my own production site and client codebases, including the awkward legacy-structure cases. Tell me which version you are coming from and roughly how the app is structured, and I will point at the places most likely to break silently — get in touch, whether the upgrade needs an audit before it ships or a full hand-off. The quiet breaks are cheap to find before deploy and expensive after.

Advertisement
Coffee cup

Enjoyed this article?

Your support helps me create more in-depth technical content, open-source tools, and free resources for the developer community.

Related Topics

Engr Mejba Ahmed

Engr Mejba Ahmed

Engr. Mejba Ahmed builds AI-powered applications and secure cloud systems for businesses worldwide. With 8+ years shipping production software in Laravel, Python, and AWS, he's helped companies automate workflows, reduce infrastructure costs, and scale without security headaches. He writes about practical AI integration, cloud architecture, and developer productivity.

Related Articles

Browse All

Comments

Leave a Comment

Comments are moderated before appearing.

Learning Resources

Expand Your Knowledge

Accelerate your growth with structured courses, verified certificates, interactive flashcards, and production-ready AI agent skills.

Sample Certificate of Completion

Sample certificate — complete any course to earn yours

Engr Mejba Ahmed

Engr Mejba Ahmed

AI assistant · trained on my work

👋

Hey there!

Quick Actions

WhatsApp Direct line to me

Chat on WhatsApp

+880 1723 741224 · Replies within the hour on working days

Popular Questions

Engr Mejba Ahmed is connected
Engr Mejba Ahmed is typing...
Engr Mejba Ahmed avatar

✉ Want me to follow up? Drop your email

Engr Mejba Ahmed avatar

📞 Connect Directly

Choose how you'd like to reach me

WhatsApp

+880 1723 741224

Email

mejba.13@gmail.com

✓ Details sent! I'll get back to you shortly.

Powered by OpenAI

335+

Blog Posts

25

AI Courses

63

Projects

Services & Expertise

Pricing & Process

Learning & Resources

Connect & Support