skip to content

In a Laravel 13 app, what do public/index.php and bootstrap/app.php each do when an HTTP request arrives?

level: juniorimportance: must knowfreq 52%

answer

  1. entry file vs builder file
  2. maintenance.php checked first
  3. vendor/autoload.php, then require bootstrap/app.php
  4. Application::configure()->...->create()
  5. handleRequest(Request::capture())

basics

~10 s

public/index.php is the entry point: it checks for maintenance mode, loads Composer's autoloader, requires bootstrap/app.php to get the application, and calls handleRequest(Request::capture()). bootstrap/app.php builds and returns that application with Application::configure()->...->create().

solid answer

~30 s

`public/index.php` is the only file the web server runs. In Laravel 13 it defines `LARAVEL_START`, includes `storage/framework/maintenance.php` if that file exists (so a site that is down can answer without booting), requires `vendor/autoload.php`, requires `bootstrap/app.php` to get the `Application` instance, and calls `$app->handleRequest(Request::capture())`. `bootstrap/app.php` returns the application: `Application::configure(basePath: dirname(__DIR__))` creates the container and a builder, chained `withRouting()`, `withMiddleware()` and `withExceptions()` calls register configuration, and `create()` hands back the app. Nothing heavy happens there: environment, config and service providers are loaded later, when the HTTP kernel handles the request. The `artisan` script requires the same `bootstrap/app.php` and calls `handleCommand()` instead.

go deeper

for a junior

Know that public/index.php is the entry point and that it gets the application from bootstrap/app.php before calling handleRequest.

for a middle

Explain the five steps of index.php, what Application::configure() sets up, and why config and providers are not loaded until the kernel handles the request.

for a senior

Use the split between building and bootstrapping to decide where early code may run, and how artisan and HTTP share one application definition.

for a principal

Judge when customising the entry or builder files is justified, for example for multiple entry points, against staying on the documented skeleton.

## Two files, two jobs A Laravel 13 request touches two small files before any of your code runs: - **`public/index.php`**, the **entry point** the web server executes for every request that is not a static file; - **`bootstrap/app.php`**, the **builder** that constructs and returns the application object. Keeping them separate lets the same application be built for HTTP (`index.php`) and for the command line (`artisan`). ## `public/index.php`, step by step The skeleton's file does five things: 1. `define('LARAVEL_START', microtime(true));` records when the request began. 2. If `storage/framework/maintenance.php` exists, it is required. That file is written when the app is put into maintenance mode, and can answer without booting the framework at all. 3. `require __DIR__.'/../vendor/autoload.php';` registers Composer's autoloader, so classes load on demand. 4. `$app = require_once __DIR__.'/../bootstrap/app.php';` gets the `Illuminate\Foundation\Application` instance. 5. `$app->handleRequest(Request::capture());` builds an `Illuminate\Http\Request` from PHP's globals and hands it over. `handleRequest()` then resolves the HTTP kernel, calls its `handle()` method, calls `send()` on the returned response and finally `terminate()`; those stages belong to the rest of the lifecycle. ## `bootstrap/app.php`: building, not booting ```php return Application::configure(basePath: dirname(__DIR__)) ->withRouting( web: __DIR__.'/../routes/web.php', commands: __DIR__.'/../routes/console.php', health: '/up', ) ->withMiddleware(function (Middleware $middleware): void { // }) ->withExceptions(function (Exceptions $exceptions): void { // })->create(); ``` - `Application::configure()` creates the application (which *is* the service container) with the given base path. If no path is passed it is inferred from `APP_BASE_PATH` or from Composer's autoloader location. - It immediately applies defaults: it binds the framework's HTTP and console kernels, registers event discovery and the `app/Console/Commands` path, and points provider registration at `bootstrap/providers.php`. - The chained `with*` calls mostly **register callbacks and settings** that run later. - `create()` simply returns the application. ## What has not happened yet | Step | When it happens | |---|---| | `.env` loaded | Kernel bootstrapping, inside `handle()` | | `config/*.php` loaded | Kernel bootstrapping | | Service providers registered and booted | Kernel bootstrapping | | Middleware and routing | After bootstrapping, inside `handle()` | | Response sent | `send()`, back in `handleRequest()` | That is why code placed at the top of `bootstrap/app.php` cannot rely on `config()` values or facades: configuration and facades are set up only when the kernel bootstraps the application. ## Where early code can and cannot run The split between *building* and *bootstrapping* decides where a line of code works: - **Top of `bootstrap/app.php`**, before `configure()`: only plain PHP and autoloaded classes. No container bindings, no config, no facades. - **Inside `withRouting()`, `withMiddleware()` or `withExceptions()` callbacks**: these are stored and run later, when the relevant part of the framework is set up, so they may use more of the framework than code at the top of the file. - **A service provider's `boot()`**: runs during bootstrapping, with configuration loaded and every eager provider registered, which is why most application setup belongs there. - **Middleware and controllers**: run for each request after bootstrapping. For a ticket-resale site that wants to read `config('tickets.max_per_order')` at startup, the right place is a provider or the code that needs it, never the top of `bootstrap/app.php`. ## The console twin The `artisan` file mirrors `index.php`: it defines `LARAVEL_START`, requires the autoloader and `bootstrap/app.php`, then calls `$app->handleCommand(new ArgvInput)` and exits with the returned status. One builder file, two entry points. ## Common misreadings - "`bootstrap/app.php` boots the providers": it builds the application; the kernel boots it during `handle()`. - "`index.php` contains routing": routing starts only after bootstrapping. - "Maintenance mode is a middleware only": the `maintenance.php` check in `index.php` runs before Laravel loads.

  • Why can storage/framework/maintenance.php answer a request without booting Laravel?
    `public/index.php` requires that file before loading the autoloader or `bootstrap/app.php`. When the app is put into maintenance mode, a small PHP script is written there that can decide to return the maintenance response itself, so a site that is down for a deploy serves its page even if the rest of the application cannot boot.
  • What does the artisan script share with public/index.php, and where do they differ?
    Both define `LARAVEL_START`, require `vendor/autoload.php` and require the same `bootstrap/app.php`, so HTTP and CLI use one application definition. `index.php` then calls `handleRequest(Request::capture())`, while `artisan` calls `handleCommand(new ArgvInput)` and exits with the status code it returns.

saying these in an interview costs you the question

  • bootstrap/app.php loads .env and config before returning
  • public/index.php defines the application's routes
  • Service providers boot when Application::configure() is called
  • artisan uses a different bootstrap file from index.php
  • Request::capture() is created after routing picks a controller