Laravel Request Lifecycle Explained Step by Step
When a request hits a Laravel app, many things happen before the final HTML or JSON response comes back.
If you are new to Laravel, this can feel confusing because you write a route and a controller, but the framework does much more in the background.
This article breaks down the full request lifecycle in simple steps, so you can understand what happens and where to debug when something goes wrong.
Why this lifecycle matters
Understanding the request lifecycle helps you:
- debug faster when routes or middleware fail
- decide where to put logic (middleware, controller, service, etc.)
- write cleaner and more maintainable Laravel apps
- perform better in interviews and real projects
The lifecycle in one quick flow
A typical Laravel request follows this order:
- Client sends HTTP request
- Web server (Nginx/Apache) forwards to
public/index.php - Laravel bootstraps the application
- Service container and service providers are loaded
- HTTP kernel receives the request
- Global and route middleware run
- Router finds matching route
- Controller or closure executes
- Response object is created
- Response is sent back to the client
- Terminable middleware runs (after response)
Now let us walk through each step clearly.
1) Request enters through public/index.php
public/index.php is the front controller of your Laravel app.
Every HTTP request goes through this file.
At a high level, it does three main things:
- loads Composer autoload
- boots Laravel application
- hands the request to Laravel’s HTTP kernel
This is why you never directly call a controller file from the browser. Laravel always uses this single entry point first.
2) Laravel bootstraps the application
After entering index.php, Laravel creates the application instance (the IoC container) using bootstrapping code.
During bootstrapping, Laravel prepares core features such as:
- environment variables (
.env) - configuration files
- error handling setup
- logging
Think of this phase as Laravel “starting the engine” before driving the request.
3) Service container is prepared
Laravel’s service container is central to the framework.
It is responsible for resolving classes and dependencies automatically.
For example, when your controller needs a service class in the constructor, Laravel injects it through this container.
class OrderController extends Controller |
You do not manually create PaymentService; the container does it for you.
4) Service providers are registered and booted
Service providers are where Laravel registers bindings, event listeners, routes (for packages), and other setup logic.
Lifecycle here has two important phases:
register(): bind things into the containerboot(): run logic after all providers are registered
Simple example:
public function register(): void |
So later, when PaymentGateway is requested, Laravel knows what concrete class to provide.
5) HTTP kernel receives the request
The HTTP kernel is responsible for handling incoming requests through a middleware pipeline and then dispatching them to the router.
In simple terms, kernel says:
- “Run middleware in order”
- “Find and execute the route”
- “Return a response”
6) Middleware pipeline runs
Middleware acts like checkpoints around your request.
Common middleware tasks:
- check maintenance mode
- start session
- protect against CSRF
- authenticate user
- throttle API requests
If one middleware stops the request (for example, unauthorized user), Laravel returns a response immediately and does not continue to the controller.
Example custom middleware:
public function handle(Request $request, Closure $next): Response |
If the check passes, request goes forward by calling $next($request).
7) Router matches a route
After middleware passes, Laravel router tries to match:
- HTTP method (
GET,POST, etc.) - URI path (
/products/10)
If route is found, Laravel resolves route parameters.
If route model binding is enabled, Laravel can convert route IDs into model instances automatically.
Route::get('/posts/{post}', [PostController::class, 'show']); |
{post} can become an actual Post model if binding is configured.
If no route matches, Laravel returns a 404 Not Found response.
8) Controller (or closure) executes business logic
Now Laravel calls your route action:
- closure route
- controller method
- single action controller (
__invoke)
This is where your application-specific logic runs:
- validate input
- call services/repositories
- read or write database
- dispatch jobs/events
- return response
Example:
public function store(StoreOrderRequest $request): JsonResponse |
9) Laravel converts return value into an HTTP response
Your route/controller may return different types:
- string
- array
- Eloquent model
ViewJsonResponseResponse
Laravel normalizes these into a proper Symfony HTTP response internally.
So this works:
return ['status' => 'ok']; |
Laravel turns it into JSON response automatically (especially in API contexts).
10) Response is sent to the browser/client
After the response object is ready, Laravel sends:
- status code (200, 201, 404, 500, etc.)
- headers
- body (HTML, JSON, file, etc.)
At this point, user sees the page or API client receives the payload.
11) Terminable middleware runs after response
Some middleware can run logic after the response is already sent.
Use cases:
- logging request/response metrics
- cleanup tasks
- non-critical background actions
This helps avoid delaying user-visible response time.
What happens when an exception occurs?
If an exception happens at any step (middleware, controller, service, database), Laravel’s exception handler catches it.
Then Laravel decides how to render the error:
- JSON error for API requests
- error view for web requests
- detailed stack trace in local/dev environment
- generic friendly message in production
This centralized handling is why you usually do not need try/catch in every controller method.
Real-world mini walkthrough
Suppose user sends:
POST /api/orders
Here is what happens:
- Request enters
public/index.php - App boots and providers load
- Kernel starts middleware pipeline
auth:sanctummiddleware checks tokenthrottle:apichecks rate limit- Router matches
/api/orders OrderController@storeexecutes- Validation happens in
StoreOrderRequest - Service creates order in database
- JSON response (
201 Created) is returned - Response goes to client
- Terminable middleware logs request duration
When you understand this flow, debugging becomes much easier.
Where to put logic (practical guidance)
A common beginner problem is putting everything in controllers.
A cleaner approach:
- middleware: cross-cutting checks (auth, permissions, locale)
- form request: validation and authorization of input
- controller: coordinate request and response only
- service class: business rules/use cases
- model: database relations/scopes
This structure aligns well with Laravel lifecycle and keeps code maintainable.
Common mistakes and how to avoid them
Heavy business logic in middleware
Keep middleware lightweight; use services for core business rules.Running expensive code in every request
Avoid unnecessary global middleware or repeated DB queries.Ignoring dependency injection
Use constructor/method injection instead ofnewinside controllers.Mixing web and API response styles
Be consistent: return JSON for APIs and views for web routes.
Quick debugging checklist for lifecycle issues
When request flow breaks, check in this order:
- Is route defined correctly? (
php artisan route:list) - Is middleware blocking the request?
- Is authentication/authorization failing?
- Is route model binding failing?
- Is validation rejecting input?
- Is any exception hidden in logs?
This order matches the lifecycle and usually finds the issue quickly.
Final thoughts
Laravel request lifecycle is not magic. It is a clear pipeline:
- bootstrap
- middleware
- routing
- controller logic
- response
- post-response tasks
Once this is clear in your mind, Laravel becomes much easier to reason about.
Do you have any questions ? Feel free to comment.