Skip to content
LaravelBlog

laravel/framework: Middleware priority fix: appendToPriorityList() now works when the anchor is the first item

LaravelBlogBot
LaravelBlogBot

Contributed by GabeSilvaDev

Part of the framework v13.36.0 release

What changed

The fix lives entirely in src/Illuminate/Foundation/Http/Kernel.php, in the shared helper addToMiddlewarePriorityRelative() that powers both addToMiddlewarePriorityAfter() and addToMiddlewarePriorityBefore() — and therefore both appendToPriorityList() and prependToPriorityList() in your bootstrap/app.php middleware configuration.

The root cause was a sentinel collision: the insertion index was initialized to 0 for "after" insertions, but 0 is also a real position in the priority list. A match against the first item ($middlewareIndex > $index → 0 > 0) never registered, so the code fell through to the "$index === 0 means nothing matched" branch and appended the middleware to the end. The strict > comparison had a second effect: a reference sitting exactly at the current insertion point was skipped too.

The patch replaces the sentinel with null:

  • $index starts as null and is only set once a reference actually matches.
  • The "after" comparison becomes is_null($index) || $middlewareIndex >= $index, so a match at the insertion point now counts.
  • The special-case array_unshift() branch is gone; appending now happens only when $index is null (nothing matched) or when the index equals the end of the list.
  • addToMiddlewarePriorityBefore() behavior is preserved — a "before" insertion at position 0 now goes through array_splice(), which is equivalent to the old array_unshift().

Two regression tests were added to KernelTest: one inserting after the first priority item, and one inserting after adjacent references. Both fail on 13.x without this patch.

The bug in practice

The default priority list starts with HandlePrecognitiveRequests, which makes the first-item case easy to hit. Say you register a tenant initializer that must run before cookies, session, and auth:

->withMiddleware(function (Middleware $middleware) {
    $middleware->appendToPriorityList(
        HandlePrecognitiveRequests::class,
        EnsureTenantIsSet::class,
    );
})

Before the fix, EnsureTenantIsSet silently landed after Authorize::class, at the very end of the priority list — running behind session, auth, and throttling. After the fix, it sits directly after HandlePrecognitiveRequests, before EncryptCookies, exactly where the call says it should.

The same defect distorted the array form when references were adjacent. On a list [A, B, C, D]:

  • addToMiddlewarePriorityAfter(['B', 'C'], 'X') produced [A, B, X, C, D] — wrong.
  • addToMiddlewarePriorityAfter(['C', 'B'], 'X') produced [A, B, C, X, D] — correct, but only by accident.

The result depended on the order you listed the references. With the >= comparison and the null sentinel, both orderings now yield [A, B, C, X, D], placing X after the highest-indexed reference.

Why it matters

Middleware ordering is load-bearing, often in security-sensitive ways: session must start before auth reads it, throttling should run before expensive work, tenant context usually needs to exist before authorization checks. A primitive that promises "insert after X" but quietly appends to the end turns those guarantees into bugs that no exception will surface. Worse, the array form was nondeterministic with respect to input order, so refactoring how you listed references could change runtime behavior. This fix makes the API do what it documents, deterministically.

Upgrade impact

There are no signature changes and no new API — this is purely a behavioral correction on the 13.x line. Practically:

  • If you never touched the priority list, nothing changes.
  • If you worked around the bug — anchoring to a later middleware, reordering the whole list by hand, or calling prependToPriorityList() to compensate — your workaround keeps working, but it may now be redundant. Revisit it and simplify if you can.
  • If an app relied on the buggy end-of-list placement (unlikely, but possible if someone noticed the wrong position and coded around it elsewhere), the affected middleware will now run earlier. Audit any appendToPriorityList() call whose anchor is HandlePrecognitiveRequests or whose references are adjacent in the list, and confirm the new, earlier position is safe for your middleware.
  • prependToPriorityList() / addToMiddlewarePriorityBefore() is functionally untouched, including its insert-at-front path.

Takeaways

  • appendToPriorityList() no longer silently appends to the end when the reference middleware is the first priority-list item.
  • Adjacent references in the array form now insert after the last of them, regardless of the order you list them.
  • The fix is internal to Kernel::addToMiddlewarePriorityRelative(); no API changes, and "before" insertions behave exactly as before.
  • When upgrading from v12.70.0, review priority-list customizations — middleware anchored near the top of the list will now genuinely run near the top.
  • Two new KernelTest cases lock the behavior in: after the first item, and after adjacent items.

Sources

More from this release

Related Articles