skip to content

In Laravel 13, where do you configure what app/Http/Kernel.php, app/Console/Kernel.php and app/Exceptions/Handler.php used to hold?

level: middleimportance: must knowfreq 50%

answer

  1. one file replaced three classes
  2. withMiddleware, withExceptions, withRouting
  3. schedules in routes/console.php
  4. app/Console/Commands still auto-registered
  5. bootstrap/providers.php for providers

basics

~10 s

Since Laravel 11 the skeleton has no kernel or handler classes. Middleware and exception handling are configured in bootstrap/app.php, schedules and closure commands go in routes/console.php, command classes in app/Console/Commands, and providers in bootstrap/providers.php.

solid answer

~30 s

The Laravel 13 skeleton has no `app/Http/Kernel.php`, `app/Console/Kernel.php` or `app/Exceptions/Handler.php`. The framework still has its own kernels, and `Application::configure()` binds them, so their jobs moved into files you edit: **middleware** is configured in `bootstrap/app.php` through `withMiddleware()`, **exception reporting and rendering** through `withExceptions()`, and **route files** through `withRouting()` (which also replaced `RouteServiceProvider`). **Scheduled tasks** go in `routes/console.php` with the `Schedule` facade, or in `withSchedule()`. **Command classes** in `app/Console/Commands` are registered automatically, and closure commands live in `routes/console.php`. **Service providers** are listed in `bootstrap/providers.php`. An app upgraded from Laravel 10 may keep its old files, because the base classes still exist.

code

php · 13 lines
php
<?php

// routes/console.php in the school-timetable app
use Illuminate\Foundation\Inspiring;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Schedule;

Artisan::command('inspire', function () {
    $this->comment(Inspiring::quote());
})->purpose('Display an inspiring quote');

// Formerly in app/Console/Kernel.php::schedule()
Schedule::command('timetable:publish')->weekdays()->at('06:00');

go deeper

for a junior

Know that a new Laravel app has no Kernel classes, and that bootstrap/app.php, bootstrap/providers.php and routes/console.php took over their jobs.

for a middle

Map each old responsibility to its new home: withMiddleware, withExceptions, withRouting, the Schedule facade in routes/console.php, auto-registered commands and providers.php.

for a senior

Work in both layouts: recognise an upgraded app still using kernel classes and decide whether and when to move it onto bootstrap/app.php.

for a principal

Decide a migration policy for old-layout apps across a portfolio, weighing churn against consistency with current documentation.

## What changed and when Before Laravel 11 every new app carried three classes that you edited to wire the framework: - `app/Http/Kernel.php` with the global middleware stack, middleware groups and aliases; - `app/Console/Kernel.php` with the `schedule()` method and command registration; - `app/Exceptions/Handler.php` with reporting and rendering callbacks. Laravel 11 introduced a **slim skeleton** that removed all three, and Laravel 13 keeps that layout. The framework still has kernels (`Illuminate\Foundation\Http\Kernel` and `Illuminate\Foundation\Console\Kernel`); `Application::configure()` binds them for you through `withKernels()`. What moved is **where you customise them**. ## The new homes | Old place | Laravel 13 place | |---|---| | `app/Http/Kernel.php` middleware lists | `bootstrap/app.php`, `withMiddleware()` | | `app/Exceptions/Handler.php` | `bootstrap/app.php`, `withExceptions()` | | `RouteServiceProvider` loading route files | `bootstrap/app.php`, `withRouting()` | | `app/Console/Kernel.php` `schedule()` | `routes/console.php` with the `Schedule` facade, or `withSchedule()` | | `app/Console/Kernel.php` `commands()` | Automatic: classes in `app/Console/Commands`, closures in `routes/console.php` | | `providers` array in `config/app.php` | `bootstrap/providers.php` | ## `bootstrap/app.php` as the wiring file The skeleton's `bootstrap/app.php` is a single chained expression: 1. `Application::configure(basePath: dirname(__DIR__))` creates the builder and registers the kernels, event discovery, the `app/Console/Commands` path and the providers from `bootstrap/providers.php`. 2. `->withRouting(web: ..., commands: ..., health: '/up')` names the route files and the health-check URL. 3. `->withMiddleware(...)` receives a `Middleware` configuration object. 4. `->withExceptions(...)` receives an `Exceptions` configuration object. 5. `->create()` returns the application that `public/index.php` and `artisan` use. What goes inside those callbacks, and the order things boot in, are separate subjects; for the layout question the point is that **one file in `bootstrap/` replaced three classes in `app/`**. ## Reading the skeleton's file The whole of the skeleton's `bootstrap/app.php` fits on one screen: ```php <?php use Illuminate\Foundation\Application; use Illuminate\Foundation\Configuration\Exceptions; use Illuminate\Foundation\Configuration\Middleware; use Illuminate\Http\Request; 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 { $exceptions->shouldRenderJsonWhen( fn (Request $request) => $request->is('api/*') || $request->expectsJson(), ); })->create(); ``` Compared with three kernel-era classes, a reader now finds routing, middleware and exception wiring in one place, and an empty `withMiddleware()` callback means the app runs the framework's default middleware groups unchanged. ## Where the other pieces landed - **Scheduled tasks**: `routes/console.php` is loaded for console commands, so `Schedule::command('timetable:publish')->daily()` written there is registered with the scheduler. - **Commands**: any command class under `app/Console/Commands` is discovered; `routes/console.php` holds `Artisan::command()` closures such as the skeleton's `inspire`. - **Providers**: `bootstrap/providers.php` returns `[AppServiceProvider::class]`; generating a provider appends to it. - **Opt-in route files**: `routes/api.php` and `routes/channels.php` appear only when you install API or broadcasting support. ## Upgraded applications An app upgraded from Laravel 10 is not forced to adopt the new layout. Its `app/Http/Kernel.php` still extends `Illuminate\Foundation\Http\Kernel`, which exists in Laravel 13, and the framework still ships `Illuminate\Foundation\Support\Providers\RouteServiceProvider`. The practical consequences: - Documentation and tutorials for Laravel 11 and later assume `bootstrap/app.php`; mixing both styles in one app confuses readers. - Check which style an app uses before adding middleware: look for `app/Http/Kernel.php` first. ## Moving an old app onto the slim layout Teams that choose to migrate an upgraded school-timetable app usually go file by file: 1. Copy the middleware customisations from `app/Http/Kernel.php` into `withMiddleware()`, keeping only what differs from the framework defaults. 2. Move reporting and rendering callbacks from `app/Exceptions/Handler.php` into `withExceptions()`. 3. Move `schedule()` entries into `routes/console.php` and delete `app/Console/Kernel.php` once commands are confirmed to load from `app/Console/Commands`. 4. Replace `RouteServiceProvider` with `withRouting()` and list the remaining providers in `bootstrap/providers.php`. 5. Rewrite `bootstrap/app.php` to the `Application::configure()` form and run the test suite after each step. Nothing forces this; the benefit is that the app then matches current documentation. ## Common misreadings - "Laravel removed middleware groups": the groups still exist; they are configured in `bootstrap/app.php`. - "Scheduling needs a Console Kernel": the scheduler reads tasks defined in `routes/console.php`. - "Custom commands must be registered by hand": classes in `app/Console/Commands` are picked up automatically.

  • How do you tell whether an existing Laravel app uses the old or the new layout?
    Look for `app/Http/Kernel.php`, `app/Console/Kernel.php` and `app/Exceptions/Handler.php`, and check `bootstrap/app.php`. The new layout returns `Application::configure(...)->with...()->create()` and has a `bootstrap/providers.php`. The old one creates `new Illuminate\Foundation\Application(...)` and binds the app's own kernel classes. Either runs on Laravel 13; add middleware in whichever place the app actually uses.
  • Where does a custom Artisan command class go in Laravel 13, and does it need registering?
    Put it in `app/Console/Commands`. `Application::configure()` calls `withCommands()`, which with no arguments registers the `app/Console/Commands` path, so classes there are discovered without being listed anywhere. A command class kept in another folder must be passed to `withCommands()` in `bootstrap/app.php`.

saying these in an interview costs you the question

  • Laravel 13 no longer has an HTTP kernel at all
  • Scheduled tasks must go in app/Console/Kernel.php
  • Each new command class must be listed in bootstrap/app.php
  • Upgrading to Laravel 11 or later forces you to delete the old kernels
  • Service providers are still listed in config/app.php in new apps