An IP whitelist check placed after auth. That was the whole bug. Every scanner bot on the internet was going through session start and a user lookup just to be told to go away. The fix was to move one line up in the Kernel.

Laravel 5.2 makes that line easier to see. Middleware groups, and the request pipeline is finally written down in one place:

protected $middlewareGroups = [
    'web' => [
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \App\Http\Middleware\VerifyCsrfToken::class,
    ],
    'api' => [
        'throttle:60,1',
        'bindings',
    ],
];

Before 5.2, sessions and CSRF were global middleware. They ran for everything, including API routes that have no use for cookies. Now web and api are two separate pipelines, and you can read each one top to bottom.

The reading matters. Middleware is an ordered list. The request enters at the top, any element can return a response and stop the chain, and then nothing below it runs, not even the controller. That is the feature. Throttling that returns 429 before authentication touches the database is throttling that works. Cheap checks first, broad checks first.

The other question is what belongs in middleware at all. My rule: middleware may know about HTTP, it must not know about the business. Rate limits, locale, auth, request logging, fine. “Can this user cancel this order” is an application service, because tomorrow the same rule is needed from a console command where there is no HTTP at all.

If you are on 5.2, open your Kernel and read the pipeline out loud. It should sound boring. Surprises there are the expensive kind. The whitelist line, for the record, was mine.