Introduction
Every HTTP request to a Laravel app follows the same journey: public/index.php, the kernel, middleware, the router, a controller, and a response. This lesson traces that journey so you can debug effectively and know where to put custom logic.
Key Concepts
- Entry point:
public/index.phpis the only PHP file the web server serves directly. - HTTP kernel: Loads configuration, registers providers, and boots the application.
- Middleware pipeline: An onion of layers that wrap the controller — each middleware runs code before and after
$next($request). - Router: Matches the request URL to a route definition and resolves the controller.
- Response: Whatever the controller returns is passed back through middleware to the browser.
Real World Context
When a page suddenly 500s in production, knowing the request lifecycle tells you where to look: the middleware list for auth issues, the router for wrong URL matches, the controller for business-logic bugs, and bootstrap/app.php for anything bootstrap-related.
Deep Dive
Understanding how Laravel processes a request helps you write better applications and debug issues effectively. Let's trace a request from browser to response.
The Journey of a Request
┌───────────────────────────────────────────────────────────────┐
│ HTTP Request │
│ GET /users?page=1 │
└───────────────────────────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────┐
│ 1. Entry Point: public/index.php │
│ - Loads Composer autoloader │
│ - Creates Application instance │
└───────────────────────────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────┐
│ 2. HTTP Kernel │
│ - Loads configuration │
│ - Registers service providers │
│ - Boots service providers │
└───────────────────────────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────┐
│ 3. Middleware Pipeline │
│ - Global middleware (HTTPS, maintenance mode) │
│ - Route middleware (auth, throttle) │
└───────────────────────────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────┐
│ 4. Router │
│ - Matches URL to route │
│ - Resolves controller/closure │
└───────────────────────────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────┐
│ 5. Controller/Action │
│ - Handles business logic │
│ - Returns response │
└───────────────────────────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────┐
│ 6. Response │
│ - Passes back through middleware │
│ - Sent to browser │
└───────────────────────────────────────────────────────────────┘
Step 1: Entry Point
Every request enters through public/index.php:
php<?php use Illuminate\Http\Request; define('LARAVEL_START', microtime(true)); // Register the Composer autoloader require __DIR__.'/../vendor/autoload.php'; // Bootstrap Laravel and handle the request (require_once __DIR__.'/../bootstrap/app.php') ->handleRequest(Request::capture());
The bootstrap/app.php file creates the Application:
php<?php use Illuminate\Foundation\Application; use Illuminate\Foundation\Configuration\Exceptions; use Illuminate\Foundation\Configuration\Middleware; return Application::configure(basePath: dirname(__DIR__)) ->withRouting( web: __DIR__.'/../routes/web.php', api: __DIR__.'/../routes/api.php', commands: __DIR__.'/../routes/console.php', health: '/up', ) ->withMiddleware(function (Middleware $middleware) { // Configure middleware here }) ->withExceptions(function (Exceptions $exceptions) { // Configure exception handling here })->create();
Step 2: The HTTP Kernel
The HTTP kernel bootstraps the application:
- Loads environment (.env file)
- Loads configuration (config/*.php)
- Registers service providers (register() methods)
- Boots service providers (boot() methods)
Step 3: Middleware Pipeline
Middleware filters requests before they reach your code. In Laravel 11+ (including Laravel 13) all middleware is registered in bootstrap/app.php via the withMiddleware() closure — the old app/Http/Kernel.php file has been removed.
php// bootstrap/app.php use Illuminate\Foundation\Application; use Illuminate\Foundation\Configuration\Middleware; return Application::configure(basePath: dirname(__DIR__)) ->withRouting(/* ... */) ->withMiddleware(function (Middleware $middleware) { // Add a middleware to every request (global middleware) $middleware->append(\App\Http\Middleware\LogRequests::class); // Append to the default 'web' group (sessions, cookies, CSRF) $middleware->web(append: [ \App\Http\Middleware\EnsureUserIsSubscribed::class, ]); }) ->create();
The default web group Laravel applies automatically contains EncryptCookies, AddQueuedCookiesToResponse, StartSession, ShareErrorsFromSession, PreventRequestForgery (the CSRF middleware, renamed from VerifyCsrfToken in Laravel 13), and SubstituteBindings. The default api group is lean: just SubstituteBindings.
Middleware executes in order:
Request → M1 → M2 → M3 → Controller → M3 → M2 → M1 → Response
Step 4: Routing
The router matches the URL to a route:
php// routes/web.php Route::get('/users', [UserController::class, 'index']); // Request: GET /users // Matched: UserController@index
Routing also runs route-specific middleware:
phpRoute::get('/dashboard', [DashboardController::class, 'index']) ->middleware('auth'); // Only authenticated users
Step 5: Controller/Action
The controller handles the request:
phpclass UserController extends Controller { public function index() { $users = User::paginate(15); return view('users.index', ['users' => $users]); } }
Dependencies are automatically injected:
phppublic function store(StoreUserRequest $request) // Auto-validated! { User::create($request->validated()); return redirect()->route('users.index'); }
Step 6: Response
The response travels back through middleware:
php// Middleware can modify responses public function handle($request, Closure $next) { $response = $next($request); // Get response from controller // Modify response $response->header('X-Custom-Header', 'Value'); return $response; }
Finally, the response is sent to the browser.
Common Pitfalls
- Adding request logic to controllers that belongs in middleware — If ten controllers all check for an API key, that is middleware's job, not each controller's.
- Forgetting that
$next($request)runs the rest of the pipeline — Code after$nextruns on the way out, not before the controller.
Best Practices
- Log at the kernel level for debugging — A custom middleware that logs the URL and duration is the fastest way to spot slow requests.
- Keep controllers thin — They should coordinate services and return views. Cross-cutting concerns live in middleware.
Summary
- Every request enters through
public/index.php. - The HTTP kernel boots the framework on each request.
- Middleware runs before AND after the controller — code after
$nextruns on the way back out. - The router maps URLs to controllers or closures.
- The controller's return value becomes the response sent to the browser.