A grounded look at what’s confirmed versus speculated for the next major release — and what to start preparing in your codebase now.
The single most important fact about Laravel 14 is the one most roundups bury or skip entirely: the Laravel team has not announced a release date or a feature list. Everything circulating right now — this post included — is inferred from commits landing on the master branch of laravel/framework, which is a legitimate source (it’s literally where Laravel 14 development happens) and a fundamentally different kind of source than a changelog. A PR merged to master today can be reverted tomorrow, reworked before release, or held back to ship as a Laravel 13 minor instead, because that’s exactly what’s already happened to several features covered below. This post is organized around that distinction deliberately: what’s structurally certain because it follows from Laravel’s own stated policy, versus what’s a real, visible signal from master that’s still genuinely subject to change, versus what’s outright speculation dressed up as a preview.
What’s Actually Certain — Because It’s Arithmetic, Not a Guess
The Q1 2027 window isn’t a leak or an announcement — it’s Laravel’s own stated release policy, applied to a pattern that’s held for years: a new major version every year, around the first quarter. Laravel 13 shipped March 17, 2026. A year later, following the same cadence, lands Laravel 14 in that same window. This is the most reliable prediction in this entire post precisely because it requires no inside information — it’s the same math anyone could run against Laravel’s public release history.
The support timeline follows from the same policy, once the release date is assumed: 18 months of bug fixes, 2 years of security fixes from release, which is why every source converges on the same numbers — bug fixes through roughly Q3 2028, security updates through roughly Q1 2029.
Version PHP Released Bug Fixes Until Security Fixes Until
11 8.2–8.4 Mar 12, 2024 Sep 3, 2025 Mar 12, 2026
12 8.2–8.5 Feb 24, 2025 Aug 13, 2026 Feb 24, 2027
13 8.3–8.5 Mar 17, 2026 Q3 2027 Mar 17, 2028
14 8.4+ Q1 2027 (expected) Q3 2028 (expected) Q1 2029 (expected)
Worth checking against your own app right now, not in Q1 2027: if you’re still on Laravel 12, bug fixes already ended August 13, 2026, and security coverage runs out February 24, 2027 — which lands right around when Laravel 14 is expected. That’s not a coincidence worth ignoring; it’s the actual planning signal in this entire post. Sitting on Laravel 12 until Laravel 14 ships means jumping two majors at once, on code that’s already outside its bug-fix window today.
PHP 8.4 as the New Floor — Confirmed, and Traceable to Its Actual Source
Laravel 14 raising its minimum PHP version to 8.4 (up from Laravel 13’s 8.3 floor) is confirmed on master via PR #59104, and it’s worth understanding why rather than treating it as an arbitrary bump: Symfony 8, one of Laravel’s own dependencies, requires PHP 8.4 as its minimum. Laravel isn’t choosing to drop PHP 8.3 support because of a new language feature it wants to use — it’s inheriting the requirement from a dependency several layers down, the same way a version floor cascades through any dependency tree.
// composer.json — the check worth running on your own app today,
// not after Laravel 14 ships and this becomes a blocker mid-upgrade
"require": {
"php": "^8.3" // if this is your floor, Laravel 14 is not a
// drop-in upgrade — it's gated on a PHP version bump first
}
The actual preparation step here isn’t waiting for Laravel 14 — it’s auditing your production PHP version now. If your hosting environment, your team’s local dev setup, or a CI pipeline pinned to PHP 8.3 hasn’t moved to 8.4 yet, that’s independent work that can start today, disconnected from anything Laravel-specific, and it’s exactly the kind of prerequisite that turns a routine major-version upgrade into a scramble when it’s discovered for the first time during the actual upgrade attempt.
Features Visible on master — Real, and Still Genuinely Provisional
These are confirmed to exist as merged PRs on the master branch as of this writing. That’s a meaningfully stronger signal than a rumor — but it’s also explicitly not a promise, since Laravel’s own pattern is that most new features ship in weekly Laravel 13 minor releases, and something only lands specifically gated to Laravel 14 when it changes a contract’s method signature or adds one, which can’t ship as a non-breaking minor release.
Route::query() — routing support for the HTTP QUERY method, a safe method (per the HTTP spec) that carries a request body, unlike GET.
Route::query('/search', function () {
return request()->input('filter');
});
Route::any() already includes QUERY, and CSRF middleware treats it as a read request, same as GET, HEAD, and OPTIONS. Laravel 13.19 already shipped the client-side half of this (Http::query()), so the routing side landing in 14 is closing a gap that’s been visible for a few releases already.
$user->authorize() — an authorize() method added directly to the Authorizable contract and trait.
// Laravel 13
Gate::forUser($user)->authorize('viewAny', Post::class);
// Laravel 14
$user->authorize('viewAny', Post::class);
Functionally identical to $user->can(), except it throws an AuthorizationException on failure instead of returning false — a smaller ergonomic change than it might sound like, but the kind of thing that removes a small, repeated bit of boilerplate (abort_unless($user->can(...))) from a huge number of controllers once it lands.
Context arrays for report() — the report() helper and the exception handler’s report() method accept an optional context array.
try {
$order->charge();
} catch (Throwable $e) {
report($e, ['order_id' => $order->id]);
}
Worth flagging now, before it becomes a breaking-change surprise: the ExceptionHandler contract itself changes to report(Throwable $e, array $context = []). Any application with a custom exception handler that overrides report() needs to add the new parameter to its own method signature — a small change, and exactly the kind that’s easy to miss during an upgrade if it isn’t specifically checked for.
Storage::fake() for on-demand disks, and consolidated whereKey()/whereKeyNot() with new orWhereKey()/orWhereKeyNot() variants, with a follow-up PR letting all four accept a subquery — both genuinely useful, both smaller in scope than the two above, both still real, merged code on master today.
Breaking Changes Already Visible — The Part Worth Auditing Your Codebase For Right Now
This is the section with the most immediate, actionable value, because every one of these is a real behavior change with a real merged PR behind it, and — unlike the release date — the specific pattern each one fixes is something you can grep your own codebase for today, regardless of when Laravel 14 actually ships.
Queue pause methods reorder their arguments. Laravel 13.25 added pausing all queues; the per-queue methods currently take the connection first.
// Laravel 13
Queue::pause('redis', 'emails');
// Laravel 14 — connection moves to the end, defaults to the default connection
Queue::pause('emails');
Queue::pause('emails', 'redis'); // still specifiable, just reordered
lazy() and chunk() stop mutating the query builder they’re called on — a genuinely subtle bug fix, not just a style change. In Laravel 13, reusing a builder after calling lazy() or chunk() on it returns wrong results, because those methods currently mutate the builder’s internal state as they paginate through it.
$query = User::query();
foreach ($query->lazy(2) as $user) {
// ...
}
$query->count(); // returns 0 in Laravel 13 — the builder was mutated
// by the lazy() call above; Laravel 14 fixes this
This one is worth actively grepping for now — a pattern of “build a query, iterate it lazily, then reuse the same builder variable for something else” is exactly the kind of code that works today by accident and needs to be identified before an upgrade, not discovered by it.
findOr() with an array of IDs starts actually calling its fallback. Currently, passing an array of IDs to findOr() never triggers the callback even when some IDs don’t exist — Laravel 14 makes it consistent with findOrFail()‘s existing array behavior.
Cache::has() and Cache::forget() finally honor arrays correctly. The docblocks have claimed array support for four years; the actual implementation hasn’t matched. Cache::has(['a', 'b']) currently returns true even if only one key exists. Cache::forget(['a', 'b']) currently does nothing and, on a Redis store, emits an “Array to string conversion” warning while silently failing to remove anything.
// If your app currently calls Cache::has() or Cache::forget() with an
// array, expecting it to work the way the docblock has always claimed —
// audit this now. The fix in Laravel 14 changes real, currently-broken
// behavior your code may be silently depending on staying broken.
MassPrunable will start including soft-deleted models in pruning, matching Prunable‘s existing behavior. If a model uses both MassPrunable and SoftDeletes today, model:prune currently force-deletes only the non-soft-deleted matches, leaving soft-deleted rows sitting in the table indefinitely. Laravel 14 closes that gap — which is the correct fix, and also means any app relying on the current (arguably buggy) behavior needs to add withoutTrashed() to its prunable() query explicitly to keep the old exclusion, rather than have it silently stop working the day the upgrade lands.
What’s Genuinely Speculation, Not Signal
Worth naming directly, since a lot of “Laravel 14 preview” content blurs this line: an exact release date more specific than “Q1 2027” is a guess, however informed — the last two release cycles both drifted later than their earliest projected windows, and there’s no reason to assume 14 won’t do the same. Any specific new headline feature not visible on master today — a new first-party package, a major architectural shift, anything framed as “Laravel 14 will probably add X” without a linked PR — is speculation, full stop, regardless of how plausible it sounds or how many other blog posts repeat it. The features section above is deliberately limited to what’s traceable to an actual merged PR number, because that’s the line between “here’s what’s happening” and “here’s what I’d guess is happening.”
The Actual Preparation Checklist
1. Audit your production PHP version today — if you're below 8.4,
that's independent work, unblocked by anything Laravel-specific,
worth starting now rather than discovering as a blocker mid-upgrade
2. Grep for lazy()/chunk() reuse — any pattern where a query builder
variable is reused after a lazy() or chunk() call is currently
working by accident, and needs identifying before Laravel 14 changes it
3. Check for Cache::has()/forget() calls with array arguments — if your
code passes arrays today, confirm what behavior you're actually
relying on, because it changes to match years-old documentation
4. If you use MassPrunable alongside SoftDeletes on any model, decide
now whether soft-deleted rows should be included in pruning —
Laravel 14 assumes yes; add withoutTrashed() explicitly if you need no
5. If you maintain a custom ExceptionHandler overriding report(),
watch for the context-array parameter addition to the contract
6. If you're on Laravel 12 today, prioritize the move to 13 now —
waiting for 14 means a two-major-version jump on code that's
already past its own bug-fix window
The One Rule
The honest way to read any “Laravel 14 preview” — this one included — is to check whether each claim traces to a specific merged PR number on master, Laravel’s own stated release policy applied as arithmetic, or neither. The first two categories are worth planning around today. The third is content written to fill a gap before there’s anything real to report, and the gap itself is the actual news: Laravel 14 doesn’t have an announced feature list yet, which means the most useful thing to do with the months before Q1 2027 isn’t guessing what’s coming — it’s auditing the parts of your own codebase, named specifically above, that are already quietly depending on behavior confirmed to change.
