Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Building Web Apps with Laravel  Module 3 – Requests, routing and middleware

How Laravel handles a request

Follow one Laravel 13 request from public/index.php through the HTTP kernel, providers, middleware and router to the response, with a run that traces it.

  • Beginner
  • 15 minutes
  • Examples run with PHP 8.5.11, Composer 2.10.3, Laravel 13.34.0 and Python 3.14.8
  • By MySmartCoPilot
Free lesson, Pro answers: 15 minutes
Free: the whole lesson. With a pass: the model answers to its 2 interview questions.

What you will learn

  • Trace a request from public/index.php through the kernel, the middleware and the router to the response
  • List the global middleware and the web and api groups of a new Laravel 13 project, and say which requests each one wraps
  • Explain why every provider's register method runs before any provider's boot method
  • Work out the order of the before and after steps of nested middleware
  • Choose between terminable middleware, defer() and a queued job for work that can wait until after the response

Before you start

What you’ll learn · Before you start

What you will learn

  • Trace a request from public/index.php through the kernel, the middleware and the router to the response
  • List the global middleware and the web and api groups of a new Laravel 13 project, and say which requests each one wraps
  • Explain why every provider's register method runs before any provider's boot method
  • Work out the order of the before and after steps of nested middleware
  • Choose between terminable middleware, defer() and a queued job for work that can wait until after the response

Before you start

On this page

When a visitor opens /notices on Pathshala’s site, 14 middleware classes of a new Laravel 13 project look at the request before your controller runs: 8 that see every request and 6 more that wrap only the routes of routes/web.php. When a request behaves strangely, such as a form field that arrives as null, a cookie that appears on one URL and not on another, or a log line that prints twice, the cause sits somewhere on this path.

You can only find it if you know the order. This lesson follows one request from the front door to the response and then past it, to the work that runs after the response has gone. You will print the real middleware lists of Laravel 13.34, trace a request through providers and middleware you write, and decide how to run work that can wait.

One front door sends every request through the kernel

Every request that is not a real file reaches a Laravel application through one file, public/index.php. The web server is set up to send it there, so there is a single entry point to protect, to log and to reason about. That file does very little: it loads Composer’s autoloader, asks bootstrap/app.php for the application, and hands the request over with $app->handleRequest(Request::capture()). The application object it receives is also the service container: The object that builds and stores the classes an application needs. Laravel's application object is the container, and providers put their bindings into it., the object that builds and stores the classes your code asks for.

The request then goes to the HTTP kernel: The class that runs a web request from start to finish: it boots the framework, sends the request through the middleware and the router, and returns a response., the class that runs a request from start to finish. Its handle method takes a request and returns a response, and between the two it boots the framework, pushes the request through the middleware and asks the router for a route. Follow one GET /notices through the whole path:

Interactive · stage by stage One request, from the web server to the response Step through the eight places a request passes, from the browser to the response that goes back.
The path of one GET /notices, step 8 of 8. Showing Browser, public/index.php, bootstrap/app.php (application object), HTTP kernel (bootstrappers run), Middleware, Router (route matched), Controller, Response (sent to the browser). In focus: Response.HTMLBrowserGET /noticespublic/index.phpthe one entry filebootstrap/app.phpapplication objectHTTP kernelbootstrappers runMiddlewareglobalRouterroute matchedControlleryour code runsResponsesent to the browser

The drawing shows the whole scene; its 8 steps are listed below.

Every step as text
  1. The web server hands over. Nginx, Apache or the development server sends every request that is not a real file to public/index.php. That one file is the front controller, so there is only one door into the application.
  2. The application is built. index.php loads Composer's autoloader and asks bootstrap/app.php for the application. That file sets the routing files, the middleware and the exception rules, and returns the application object, which is also the service container.
  3. The kernel takes the request. index.php passes the request to the HTTP kernel through handleRequest. The kernel first runs its bootstrappers, which load the environment and configuration and then register and boot the service providers.
  4. Global middleware wraps it. The request goes through the global middleware, which sees every request, even one that matches no route. Each middleware may change the request, stop it with a response of its own, or pass it inward.
  5. The router chooses a route. Inside the global layer the router matches the method and path against the route files. A match brings that route's own middleware, such as the web group, and a miss becomes a 404 response.
  6. Your controller runs. The route's closure or controller action runs and returns something, a view, an array or a response object. Laravel turns whatever it returned into a response.
  7. The response travels back out. The response passes back out through the same middleware, in the opposite order, so each one can add headers or change the body on the way out.
  8. Terminate runs last. After the response is sent, the kernel calls terminate. Terminable middleware and deferred functions run here, so the visitor is not kept waiting for them.

The figure shows the part of the path that does not change from one application to another. What you control is what you put into it: the routing files and middleware that bootstrap/app.php registers, the providers listed in bootstrap/providers.php, and the controller that finally answers. The documentation calls this whole journey the request lifecycle: The path one request takes through an application: the front controller, the kernel, the middleware, the router and the controller, then the response and the work that runs after it..

Find the front door

Open public/index.php in your Pathshala project and find the two lines that matter: the require_once of bootstrap/app.php, and the call to handleRequest. Everything else in the file is housekeeping.

Global middleware sees every request and a group sees only its routes

A middleware: A class that sits between the web server and the controller. It can inspect or change a request, stop it with its own response, or pass it inward and then change the response on the way out. is a class that sits between the web server and your controller. It receives the request, can look at it or change it, and then either passes it inward or stops it with a response of its own. The kernel keeps one list of global middleware: A middleware in the HTTP kernel's own list. It wraps every request, including one that matches no route., which wraps every request, and the router keeps named middleware group: A named list of middleware, such as web or api, that the router attaches to every route loaded into the group., which wrap only the routes they are attached to. Print what a new Laravel 13.34 project holds:

The global middleware, the groups and the aliases Python · stack_session.py
from session import run

# Print the global middleware, the groups and the aliases of a new Laravel 13 project.
run(
    """
php middleware_stack.php
""",
    app=True,
    overlay=("stack-probe",),
)

Output

$ php middleware_stack.php
global, in order:
  ValidatePathEncoding
  InvokeDeferredCallbacks
  TrustProxies
  HandleCors
  PreventRequestsDuringMaintenance
  ValidatePostSize
  TrimStrings
  ConvertEmptyStringsToNull
group web, in order:
  EncryptCookies
  AddQueuedCookiesToResponse
  StartSession
  ShareErrorsFromSession
  PreventRequestForgery
  SubstituteBindings
group api, in order:
  SubstituteBindings
aliases:
  auth             Authenticate
  auth.basic       AuthenticateWithBasicAuth
  auth.session     AuthenticateSession
  cache.headers    SetCacheHeaders
  can              Authorize
  guest            RedirectIfAuthenticated
  password.confirm RequirePassword
  precognitive     HandlePrecognitiveRequests
  signed           ValidateSignature
  throttle         ThrottleRequests
  verified         EnsureEmailIsVerified

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 stack_session.py

Read the output top down. Eight global middleware run first, in this order: ValidatePathEncoding, InvokeDeferredCallbacks, TrustProxies, HandleCors, PreventRequestsDuringMaintenance, ValidatePostSize, TrimStrings and ConvertEmptyStringsToNull. The last two change input before your code sees it, so a name sent as " Meera " arrives as "Meera" and an empty text box arrives as null. The requests lesson shows what that does to your validation rules.

The web group adds six more, for pages meant for browsers: EncryptCookies, AddQueuedCookiesToResponse, StartSession, ShareErrorsFromSession, PreventRequestForgery and SubstituteBindings. The api group holds only SubstituteBindings, which is why a JSON API has no session and no cookies unless you add them. The aliases at the end are short names, such as throttle and auth, that you can attach to a route without writing the class. The routing lesson and the middleware lesson of this module use them.

Which requests does the web group wrap? Only those that reach a route registered in routes/web.php. Start the development server and ask for three URLs to see the difference:

Headers of a page, the health check and an unknown path Python · front_door_session.py
from session import serve

# Real HTTP through public/index.php: a page in the web group, the health check and a path no route handles.
serve(
    """
curl -s -D - -o /dev/null http://127.0.0.1:8311/
curl -s -D - -o /dev/null http://127.0.0.1:8311/up
curl -s -D - -o /dev/null http://127.0.0.1:8311/missing
""",
    app=True,
    port=8311,
)

Output

# Terminal 1: start the server
$ php artisan serve --host=127.0.0.1 --port=8311 --no-reload
# Terminal 2: send requests
$ curl -s -D - -o /dev/null http://127.0.0.1:8311/
HTTP/1.1 200 OK
Host: 127.0.0.1:8311
Connection: close
X-Powered-By: PHP/8.5.11
Content-Type: text/html; charset=utf-8
Cache-Control: no-cache, private
Date: …
Set-Cookie: XSRF-TOKEN=…; expires=…; Max-Age=…; path=/; samesite=lax
Set-Cookie: laravel-session=…; expires=…; Max-Age=…; path=/; httponly; samesite=lax
$ curl -s -D - -o /dev/null http://127.0.0.1:8311/up
HTTP/1.1 200 OK
Host: 127.0.0.1:8311
Connection: close
X-Powered-By: PHP/8.5.11
Cache-Control: no-cache, private
Date: …
Content-Type: text/html; charset=utf-8
$ curl -s -D - -o /dev/null http://127.0.0.1:8311/missing
HTTP/1.1 404 Not Found
Host: 127.0.0.1:8311
Connection: close
X-Powered-By: PHP/8.5.11
Cache-Control: no-cache, private
date: …
Content-Type: text/html; charset=utf-8
# Terminal 1 showed this, then Ctrl+C stopped the server:
   INFO  Server running on [http://127.0.0.1:8311].

  Press Ctrl+C to stop the server

  … / ................................................. ~ …
  … /up ............................................... ~ …
  … /missing .......................................... ~ …

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 front_door_session.py

The home page answers with two Set-Cookie headers, XSRF-TOKEN and laravel-session, the work of the web group’s cookie and session middleware. The health route /up and the unknown /missing answer without any cookie, although both responses still pass through the global middleware. The skeleton registers /up outside the web group, so a monitoring check does not start a session for each ping.

Checkpoint

Check: One front door and the kernel · Global middleware and middleware groups

  1. Question 1 of 2 Which file receives every request that the web server sends to a Laravel application?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: public/index.php

    The web server sends every request to public/index.php. That file loads the autoloader, gets the application from bootstrap/app.php and calls handleRequest, which passes the request to the HTTP kernel.

  2. Question 2 of 2 Pathshala's monitoring tool calls /up every minute, and /up is the health route the skeleton registers. Which of these middleware runs for that request?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: TrimStrings, a global middleware

    The eight global middleware wrap every request, even one that matches no route. The web group wraps only the routes loaded into it. The health route is registered on its own, so a monitoring ping gets the global layer and no session.

Providers register everything before they boot anything

A service provider: A class that sets one part of the application up. Its register method binds services into the container, and its boot method uses them once every provider has registered. is a class that sets one part of the application up. Nearly every feature of the framework, from the database to validation, arrives through a provider, and your application lists its own in bootstrap/providers.php. Each provider has two methods with different jobs: register only binds things into the container, and boot uses them. The kernel calls register on every provider first and boot on each of them afterwards.

To watch the two phases, the project below lists two providers. AppServiceProvider is first in the list, and its register method tries to build a FeeCalendar, an interface that only the second provider binds. Its boot method asks for the same calendar a moment later. A small Trace class prints one numbered line for each event, and the harness sends two requests through the real kernel:

Providers, middleware and the controller, in the order they ran Python · trace_session.py
from session import run

# One request that matches a route and one that does not, with every step of the lifecycle traced.
run(
    """
php trace_request.php /notices /missing
""",
    app=True,
    overlay=("trace-probe",),
)

Output

$ php trace_request.php /notices /missing
== GET /notices
  1  AppServiceProvider::register
  2    register() cannot use a later binding: BindingResolutionException
  3  PathshalaServiceProvider::register binds FeeCalendar
  4  AppServiceProvider::boot, next due day is the 5th of next month
  5  PathshalaServiceProvider::boot
  6  middleware global: before
  7  middleware web group: before
  8  controller: building the notice list
  9  middleware web group: after, status 200
 10  middleware global: after, status 200
 11  response sent
 12  deferred callback runs
 13  AuditAfterResponse::terminate, status 200
== GET /missing
 14  middleware global: before
 15  middleware global: after, status 404
 16  response sent
 17  AuditAfterResponse::terminate, status 404

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 trace_session.py

Lines 1 to 5 belong to the providers. Line 2 is the failure: while the first provider registers, nothing has bound FeeCalendar, so the container throws a BindingResolutionException. Line 4 is the same request for the calendar one phase later, and it works, because line 3 has registered the second provider in between. Both providers ran during the first request, because the kernel boots the application when it first handles one.

Interactive · message by message Register for everyone, then boot for everyone Step through two providers, listed in bootstrap/providers.php in this order, as the first request starts the application.
Every register runs before any boot, step 4 of 4. Messages so far: Kernel to App: register(); Kernel to Fees: register(); Kernel to App: boot(); Kernel to Fees: boot().KernelAppdue day 5Feesboundregister()register()boot()boot()

The drawing shows the whole scene; its 4 steps are listed below.

Every step as text
  1. The first register. The kernel creates both providers, then calls register on the first one. That register tries to use the fee calendar, which only the second provider binds, and the container answers with a BindingResolutionException.
  2. The second register. The kernel calls register on the second provider. It binds the FeeCalendar interface to MonthlyCalendar, so the container now knows how to build one.
  3. The first boot. Every provider has registered, so the kernel starts the boot calls. The first provider asks for a FeeCalendar and gets one, although it was listed before the provider that binds it.
  4. The second boot. The second provider boots last. Its boot may rely on any binding made by any provider, which is why routes, observers, gates and macros belong in boot and not in register.

This is why routes, gates, event listeners and macros belong in boot. A register method that touches a service another provider supplies works or fails depending on the order of the list, and that kind of bug appears only after someone reorders a file.

Key idea

register is for binding and boot is for using. Because all the register calls finish before the first boot starts, a boot method can rely on every binding of every provider.

Middleware nests, so the last to go in is the first to come out

Now look at lines 6 to 13 of the trace, the request for /notices. Two copies of one Trace middleware are registered. The first is global, added with $middleware->append(...). The second is added to the web group with $middleware->web(append: [...]). A text after a colon, as in Trace::class.':global', reaches the middleware as an extra argument, which is how one class prints two different labels.

The prints come in the shape of nested function calls. Each middleware prints its “before” line, calls $next($request) to run everything deeper, and prints its “after” line when that call returns. Replay the lines one at a time:

Interactive · stage by stage Lines 6 to 13 of the trace, replayed Step through the lines the trace printed for GET /notices, in order.
The order of one request through nested middleware, step 7 of 7. Showing Global, Web group, Controller, Web group, Global, Response sent, Terminate. In focus: Terminate.GlobalbeforeWeb groupbeforeControllertrace line 8Web groupafterGlobalafterResponse senttrace line 11Terminatetrace lines 12 and 13

The drawing shows the whole scene; its 7 steps are listed below.

Every step as text
  1. Global middleware, going in. The first middleware of the kernel's list prints its before line and calls the next one. Nothing deeper has run yet, so this line comes first.
  2. The group, going in. The route matched, so the web group's middleware runs inside the global layer. Its before line is second because it is nested one level deeper.
  3. The controller answers. The innermost layer is the route's closure. It builds the notice list and returns a response with status 200.
  4. The group, coming out. Control returns to the web group first. It sees the response and its status, which is why its after line is printed before the global one.
  5. Global middleware, coming out. The outermost layer sees the response last. Middleware around middleware behaves like nested function calls, so the last to go in is the first to come out.
  6. The response is sent. handle has returned, so the response goes to the browser. Everything printed so far happened while the visitor was waiting.
  7. Terminate and deferred work. The kernel calls terminate. The deferred callback runs first and the audit middleware's terminate method second, because the kernel terminates its list in the order it was listed.

The global middleware printed first on the way in and last on the way out, because it is the outermost layer. The web group printed second in and second out. The controller, line 8, ran at the centre. Any middleware that adds a header to the response does so on the way out, after its $next call, and the layers nearest to the controller get the first chance to change it.

A middleware that never calls $next ends the request at its own layer. It returns a response itself, as an authentication middleware does when it redirects a visitor to the login page. Nothing deeper runs, and every layer around it still prints its “after” line, because they are still waiting for the result of their own $next call. The /missing request in the trace shows the same shape: the router found no route, so the controller layer and the web group are absent, while the global pair prints status 404.

Writing a response header before $next

A middleware cannot set a response header before it calls $next($request), because the response does not exist yet. Call $next($request) first, keep its result in $response, set the header on that object and return it.

Checkpoint

Check: Register, then boot · Nested middleware and the order of before and after

  1. Question 1 of 2 The first provider's register method resolves a service that only the second provider binds. What happens?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: The container cannot resolve it yet and throws an exception

    register runs on each provider in list order, so a later provider's bindings do not exist yet. Moving the use into boot fixes it, because boot starts only after every register has finished.

  2. Question 2 of 2 Middleware A and then B are attached to a route, and both print a line before and after calling $next. Which order do the four lines print in?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: A before, B before, B after, A after

    Middleware wraps like nested function calls. The first listed layer opens first and closes last, so the order is A before, B before, the controller, B after, A after.

Work that can wait runs after the response with terminate or defer

Some work does not need to finish before the visitor sees the page: writing an audit line, recording a metric, sending a statistics ping. Laravel gives two places to put it that run after the response has been sent. The first is a terminable middleware: A middleware with a terminate method, which the kernel calls once the response has been sent, so slow work does not make the visitor wait., which has a terminate method that the kernel calls once the response has gone. In the trace it is AuditAfterResponse. The second is defer(), which takes a closure, the deferred function: A closure passed to defer(). It runs after the response has been sent, and by default only when the response status is below 400., and runs it at the same moment.

Lines 12 and 13 of the trace show both at work. The deferred callback printed first and the middleware’s terminate second, because the deferred callbacks are run by a global middleware that sits earlier in the list than the audit middleware. The documentation notes that terminate is called automatically when the web server uses FastCGI, which is how most production PHP runs. The harness above calls it by hand, as handleRequest does, so it is the real kernel but not a web server.

There is a catch. A deferred function runs only when the response is successful, below status 400, unless you chain always(). Ask for a route that defers two callbacks, one of them with always(), and answers 403:

A 403 response with two deferred callbacks Python · after_response_session.py
from session import run

# A route that answers 403 after it deferred two callbacks, one of them with always().
run(
    """
php trace_request.php /closed
""",
    app=True,
    overlay=("trace-probe",),
)

Output

$ php trace_request.php /closed
== GET /closed
  1  AppServiceProvider::register
  2    register() cannot use a later binding: BindingResolutionException
  3  PathshalaServiceProvider::register binds FeeCalendar
  4  AppServiceProvider::boot, next due day is the 5th of next month
  5  PathshalaServiceProvider::boot
  6  middleware global: before
  7  middleware web group: before
  8  middleware web group: after, status 403
  9  middleware global: after, status 403
 10  response sent
 11  always-deferred callback runs
 12  AuditAfterResponse::terminate, status 403

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 after_response_session.py

Line 8 shows the web group’s “after” with status 403, and the plain deferred callback never prints. Only the callback marked always() runs, on line 11, and the audit middleware’s terminate still sees the 403 on line 12. A closure that reports a metric usually wants always() for exactly this reason: a failed request is the one you most want to count.

Neither of the two retries. The kernel runs every deferred function inside rescue(), which reports an exception to the log and moves on, so a failed callback is dropped. Work that must not be lost belongs in a queued job, which a separate worker process can retry. Choose by answering the two questions of the table:

Interactive · answer and compare Choosing where work after the response runs Answer the two questions. The table marks the options that fit, and gives the reason under each one that does not.
Must the work be tried again if it fails?
Does the work belong to every request, or to one route?

Answer the questions above: the table then marks what fits your case and says why the rest does not.

Three ways to run work after the response against four things the work needs
Way Where it runs On a 4xx or 5xx response If it throws Needs a worker
defer() What it is: a closure run after the response, in the same process Good after the response, by the same PHP process Mixed skipped unless you chain always() Poor the error is reported and the callback is dropped, with no retry Good none
Terminable middleware What it is: a terminate method that every matched request calls Good after the response, for every request that passes the middleware Good runs, and receives the error response to inspect Poor the request has ended and nothing retries the work Good none
Queued job What it is: a job that a separate worker process picks up Mixed when a worker takes it, often a moment later Good runs when the code that dispatched it chose to dispatch Good the job can retry, and a failure is recorded Poor a long-running worker must be started and kept alive
How the answers decide
  • “Must the work be tried again if it fails?” Yes, losing it is not acceptable: rules out defer() and Terminable middleware. Neither runs again after an error, so work that must not be lost needs a queue and its retries.
  • “Must the work be tried again if it fails?” No, losing it is acceptable and “Does the work belong to every request, or to one route?” Every request that gets through: adds a caveat to defer(). A callback must be deferred from code on each path, so one middleware with a terminate method is the simpler place for work every request needs.
  • “Does the work belong to every request, or to one route?” One route or action: adds a caveat to Terminable middleware. A terminable middleware can be attached to a single route, but a defer call beside the code it follows is easier to read.

Key idea

A terminable middleware and a deferred function run after the visitor has the response, in the same process, with no retry. A queued job is the choice when the work must survive a failure.

Checkpoint

Check: Work after the response

  1. Question 1 of 2 A controller calls defer(fn () => log('counted')) and then aborts with a 403. When does the closure run?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Never, unless you chain always() onto the defer call

    A deferred function runs after the response only when the status is below 400, unless it chains always(). On the 403 route the plain callback printed nothing and the always() callback ran.

  2. Question 2 of 2 Pathshala must send a fee receipt by email after a payment, and an email that fails has to be tried again later. Where should the sending run?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: In a queued job that a worker retries

    Deferred functions and terminate hooks run once, in the same process, without a retry. Work that has to survive a failure belongs on a queue, where a worker can run it again.

Practise

Trace a request in your own Pathshala project, and check each step with the files you wrote:

  1. Create a middleware with php artisan make:middleware LogRequest, and make it write one line before $next and one after, each with the HTTP method and the path.
  2. Register it as global middleware with $middleware->append(LogRequest::class) in bootstrap/app.php, then add a second copy to the web group with $middleware->web(append: [LogRequest::class]). Request /notices, /up and a URL that does not exist, and write down which requests print one pair of lines and which print two.
  3. Add a defer() call to a controller, make the controller call abort(403) after it, and note whether the deferred code ran. Then add ->always() and run the request again.

Then work through the self-check, which asks about each step of the path.

Practice that adapts to you

Practise this lesson

15 questions about 5 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 15 What does public/index.php do?

    Choose one answer.

    Show the answer: Question 1 of 15

    Answer: It builds the application from bootstrap/app.php and passes the request to the HTTP kernel

    The file is a thin front door. It loads the autoloader, gets the application and calls handleRequest, which captures the request and gives it to the kernel.

  2. Question 2 of 15 Which description of the HTTP kernel's handle method is accurate?

    Choose one answer.

    Show the answer: Question 2 of 15

    Answer: It takes a request and returns a response, running the bootstrappers, the middleware and the router in between

    The documentation describes the kernel as a black box that represents the application: a request goes in and a response comes out. Sending the response and calling terminate are separate steps that handleRequest performs.

  3. Question 3 of 15 Pathshala needs to add a header to every response and write one log line per request. What does the single front door make possible?

    Choose one answer.

    Show the answer: Question 3 of 15

    Answer: One global middleware sees every request, so one place does both jobs

    Because there is one entry point and one kernel, a global middleware is the right place for work that every request needs, such as a header or a log line.

  4. Question 4 of 15 How many global middleware does a new Laravel 13.34 project run on every request?

    Type a number.

    Show the answer: Question 4 of 15

    Answer: 8 middleware

    The recorded list has eight: ValidatePathEncoding, InvokeDeferredCallbacks, TrustProxies, HandleCors, PreventRequestsDuringMaintenance, ValidatePostSize, TrimStrings and ConvertEmptyStringsToNull.

  5. Question 5 of 15 A JSON route loaded with the api group sets no session cookie. Why?
    Show the answer: Question 5 of 15

    Answer: The api group holds only SubstituteBindings, with no session or cookie middleware

    A group is just a named list. The web list starts a session, and the api list contains one middleware that has no session in it.

  6. Question 6 of 15 A form posts an empty "notes" box and the controller expects a string. A global middleware turns the empty text into null. Which one, and what must the validation rule allow?

    Choose one answer.

    Show the answer: Question 6 of 15

    Answer: ConvertEmptyStringsToNull, so the rule must allow null

    TrimStrings and ConvertEmptyStringsToNull are global, so they change the input before the controller reads it. An empty text box arrives as null, and the rule needs to be nullable to accept it.

  7. Question 7 of 15 Where in a service provider do you register a gate or an event listener?

    Choose one answer.

    Show the answer: Question 7 of 15

    Answer: In boot

    register binds, boot uses. A gate, a listener, a macro or a view composer needs the container to be complete, so it goes in boot.

  8. Question 8 of 15 An app works until a teammate swaps two lines in bootstrap/providers.php, and then a binding exception appears. Which mistake fits best?

    Choose one answer.

    Show the answer: Question 8 of 15

    Answer: A register method uses a service that a later provider binds

    A bug that depends on the order of providers points to register code that uses a service another provider supplies. The cure is to move that use into boot.

  9. Question 9 of 15 Your provider's register method calls config('pathshala.fees') and then binds a service that needs it. Which change keeps the order from mattering?

    Choose one answer.

    Show the answer: Question 9 of 15

    Answer: Bind a closure in register and read the config when the service is first built

    A binding closure delays the work until the container resolves the service, by which time every provider has registered. It makes the result independent of the order of the list.

  10. Question 10 of 15 A global middleware prints "g before" and "g after", and a route middleware prints "r before" and "r after". The controller prints "c". In what order do the five lines appear?

    Choose one answer.

    Show the answer: Question 10 of 15

    Answer: g before, r before, c, r after, g after

    Global middleware wraps route middleware, and the controller is the centre. The order is g before, r before, c, r after, g after.

  11. Question 11 of 15 A route middleware returns a redirect without calling $next. Which statement is true?

    Choose one answer.

    Show the answer: Question 11 of 15

    Answer: The controller does not run, and the outer middleware still print their after lines

    A middleware that returns without calling $next short-circuits the request. The layers around it still run their after code, with the redirect as the response.

  12. Question 12 of 15 A middleware must add an X-Request-Id header to every response. Where does the line go?

    Choose one answer.

    Show the answer: Question 12 of 15

    Answer: After $next($request), on the response it returned

    Response headers are written on the way out. Call $next first, then set the header on its result and return that response.

  13. Question 13 of 15 When does the kernel call a terminable middleware's terminate method?

    Choose one answer.

    Show the answer: Question 13 of 15

    Answer: After the response has been sent to the browser

    handleRequest calls handle, sends the response and then calls terminate on the kernel, which calls each terminable middleware's terminate method.

  14. Question 14 of 15 A route defers a statistics call with defer() and ends with abort(404). What do you add so the statistic is still counted?

    Choose one answer.

    Show the answer: Question 14 of 15

    Answer: Chain always() onto the defer call

    By default a deferred function runs only for a response below 400. Chaining always() overrides that, as the /closed route in the trace shows.

  15. Question 15 of 15 Pathshala counts page views in a cheap in-memory counter, and losing a count now and then is fine. Every request, successful or not, should be counted. What is the simplest fit?

    Choose one answer.

    Show the answer: Question 15 of 15

    Answer: A terminable middleware that increments the counter in terminate

    A cheap counter that may lose an occasional count is the case for terminate: one class covers every request, including errors, with no retry machinery.

Interview questions

Warm-up: in which order do the register and boot methods of service providers run, and why does the order matter?

The kernel creates every provider listed in bootstrap/providers.php and calls register on each of them. Only when all of them have registered does it call boot on each of them, in the same list order. The split exists so that a provider’s boot can depend on any binding made by any provider, whatever order they are listed in. It also tells you what each method is for. register should only bind things into the container, because a service from a later provider may not exist yet, and an attempt to use one fails depending on the order of the list. boot is where you use the container: you define gates, listen for events, add macros and register view composers there.

Three longer questions come from this path:

Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.

  1. Fresher to mid

    A teammate adds a SecurityHeaders middleware to the web group. The monitoring check on /up and the JSON routes under /api come back without the headers. Explain why, and say where you would register it instead.

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Mid to senior

    Each page request writes two audit lines, and each JSON request writes one. The audit middleware is registered once in the code you wrote. Find the cause, fix it, and describe the check that keeps it fixed.

    Model answer, Pro. First step and follow-ups

Flashcards · 5 cards

Recall it, then check

Say the answer to yourself before you look: recalling it is what makes it stick.

  1. Where does the whole path of a Laravel web request begin?

    In public/index.php, which builds the application from bootstrap/app.php and hands the request to the HTTP kernel. Every request starts in that one place.

  2. Which middleware layers does a new Laravel 13 project put around its routes?

    Eight global middleware that wrap every request, a web group of six for browser routes and an api group of one. The /up health route and URLs that match no route get only the global layer.

  3. In which order do service providers' register and boot methods run?

    The kernel calls register on every provider in bootstrap/providers.php first, and only then boot on each, in the same order. So register should only bind services, and boot can use any binding of any provider.

  4. In what order do nested middleware run on the way in and on the way out?

    Like nested function calls: the first layer in is the last layer out. A middleware that returns a response without calling $next stops every deeper layer, while the outer layers still run their code after $next.

  5. What do terminate and defer() have in common, and when is neither enough?

    Both run work after the visitor has the response, in the same PHP process. A deferred function is skipped on a 4xx or 5xx response unless it chains always(), and neither retries, so work that must not be lost belongs in a queued job.

Key takeaways

  • Every request enters through public/index.php, which builds the application from bootstrap/app.php and hands the request to the HTTP kernel, so there is one place where the whole path begins.
  • A new Laravel 13.34 project has eight global middleware that wrap every request, a six-middleware web group for browser routes and a one-middleware api group, and /up and unmatched URLs get only the global layer.
  • Every provider’s register method runs before any provider’s boot method, so register only binds services and boot is where you use them.
  • Middleware nests like function calls, so the first layer in is the last layer out, and a middleware that returns without calling $next stops everything deeper while the outer layers still run their after code.
  • terminate and defer() run after the response has been sent, a deferred function is skipped on a 4xx or 5xx response unless it chains always(), and neither retries, so work that must not be lost goes to a queue.

Terms in this lesson

deferred function
A closure passed to defer(). It runs after the response has been sent, and by default only when the response status is below 400.
global middleware
A middleware in the HTTP kernel's own list. It wraps every request, including one that matches no route.
HTTP kernel
The class that runs a web request from start to finish: it boots the framework, sends the request through the middleware and the router, and returns a response.
middleware
A class that sits between the web server and the controller. It can inspect or change a request, stop it with its own response, or pass it inward and then change the response on the way out.
middleware group
A named list of middleware, such as web or api, that the router attaches to every route loaded into the group.
request lifecycle
The path one request takes through an application: the front controller, the kernel, the middleware, the router and the controller, then the response and the work that runs after it.
service container
The object that builds and stores the classes an application needs. Laravel's application object is the container, and providers put their bindings into it.
service provider
A class that sets one part of the application up. Its register method binds services into the container, and its boot method uses them once every provider has registered.
terminable middleware
A middleware with a terminate method, which the kernel calls once the response has been sent, so slow work does not make the visitor wait.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.