In Laravel, what is Fortify, what does the features array in config/fortify.php control, and what does 'views' => false change?
answer
- headless: routes and controllers, no UI
- starter kits use it internally
- features array switches route groups on
- views false drops the GET screens
- not Sanctum: no tokens, no SPA cookies
basics
~20 sFortify is Laravel's headless authentication backend: it registers login, registration, reset, verification, two-factor and passkey routes and controllers but ships no UI. The features array decides which route groups exist; 'views' => false removes the GET routes that return screens.
solid answer
~40 sFortify (`laravel/fortify`, 1.40 here) is a frontend-agnostic auth backend: install it with `composer require laravel/fortify` and `php artisan fortify:install`, then point your own screens at the routes it registers. The current starter kits use it internally. In `config/fortify.php` the `features` array is the switchboard: each entry, such as `Features::registration()`, `Features::resetPasswords()`, `Features::emailVerification()`, `Features::updateProfileInformation()`, `Features::updatePasswords()`, `Features::twoFactorAuthentication([...])` or `Features::passkeys([...])`, registers that feature's routes; leave one out and its routes do not exist. Login, logout and password confirmation are always registered. `'views' => true` also registers GET routes such as `/login` and `/register` that render whatever you bind with `Fortify::loginView()` and friends; `'views' => false` drops those GET routes for an SPA that draws its own screens, while the POST endpoints stay. Fortify is not Sanctum: Sanctum authenticates requests, Fortify implements the flows.
go deeper
Recall that Fortify is a headless auth backend with routes but no UI, and that the features array turns flows such as registration and two-factor on or off.
Explain the GET-view versus POST-action split, what views false removes, which routes exist regardless of features, and why Fortify and Sanctum often appear together.
Decide which features a product should expose, and know the stub differs from the package defaults, for example email verification commented out.
Weigh adopting Fortify's flows against owning hand-written auth, counting upgrade cost, security review scope and how much the team will customise.
## What Fortify is **Laravel Fortify** is a **headless authentication backend**: a package that registers the routes and controllers for Laravel's account flows (login, logout, registration, password reset, email verification, profile and password updates, password confirmation, two-factor authentication and passkeys) without shipping any HTML, CSS or JavaScript. You bring the screens; Fortify brings the endpoints behind them. The current Laravel starter kits (React, Vue, Svelte and Livewire) are built on Fortify, so an application created from a kit already has it installed and configured. Without a kit you install it yourself: 1. `composer require laravel/fortify` 2. `php artisan fortify:install`, which publishes `config/fortify.php`, the action classes in `app/Actions/Fortify`, `app/Providers/FortifyServiceProvider.php` and the migrations, and registers the provider in `bootstrap/providers.php`. 3. `php artisan migrate`. The docs are explicit that Fortify is optional: you can build the same flows with Laravel's auth services by hand. ## The features array `config/fortify.php` has a `features` key holding an array of `Laravel\Fortify\Features` calls. Each call returns a feature name, and Fortify's route file only registers a feature's routes when that name is in the array. The published stub in Fortify 1.40 enables: | Feature | Routes it adds (examples) | |---|---| | `Features::registration()` | `POST /register` | | `Features::resetPasswords()` | `POST /forgot-password`, `POST /reset-password` | | `Features::updateProfileInformation()` | `PUT /user/profile-information` | | `Features::updatePasswords()` | `PUT /user/password` | | `Features::twoFactorAuthentication([...])` | `/two-factor-challenge`, `/user/two-factor-authentication` and more | | `Features::passkeys([...])` | `/passkeys/login`, `/user/passkeys` and more | `Features::emailVerification()` is present but **commented out** in the stub, even though the package's own default config enables it; applications get the stub. Login (`/login`), logout (`/logout`) and the password-confirmation endpoints are registered regardless of the array. Some features take options. The stub passes `['confirm' => true, 'confirmPassword' => true]` to `twoFactorAuthentication` and `['confirmPassword' => true]` to `passkeys`. ## `'views' => false` Fortify splits each screen into a **GET route that returns a view** and a **POST (or PUT/DELETE) route that does the work**. With `'views' => true` (the default) it registers the GET routes too: `/login`, `/register`, `/forgot-password`, `/reset-password/{token}`, `/email/verify`, `/user/confirm-password` and `/two-factor-challenge`. Each returns whatever you bound, for example `Fortify::loginView(fn () => view('auth.login'))`; if you never bind one, the route has nothing to render. Setting `'views' => false` removes those GET routes. It suits a single-page application that renders its own screens and only calls the endpoints. One catch from the docs: the reset-password notification builds its link from a route named `password.reset`, so an SPA that disables views must still define a route with that name. ## Fortify versus Sanctum The two are complementary, not competing: - **Fortify** implements the **flows**: registering, logging in, resetting, enabling two-factor authentication. - **Sanctum** implements **request authentication** for SPAs and API tokens; it has no registration or reset routes. An SPA backend commonly uses both: Fortify for the flows on the `web` guard, Sanctum so the SPA's later API calls are authenticated with the session cookie. ## In a tax-filing application A tax-filing product that wants its own Vue front end but not a starter kit's screens would install Fortify, set `'views' => false`, keep `registration`, `resetPasswords` and `twoFactorAuthentication`, and drop `updateProfileInformation` if profile edits go through a custom, audited endpoint instead. ## Common misreadings in interviews - **"Fortify is a starter kit."** It is the backend the kits sit on; it has no screens of its own. - **"Removing a feature hides it."** Removing a feature removes its routes; there is nothing to hide. - **"`views => false` turns Fortify off."** Only the GET screens go; every POST, PUT and DELETE endpoint stays. - **"The package defaults are what my app runs."** The app runs the published stub, which differs: for example `lowercase_usernames` is `true` in the stub and `false` in the package, and email verification is commented out in the stub. - **"Fortify replaces Laravel's auth services."** It calls them: guards, the password broker and the hasher are all Laravel's.
- What happens to the login screen if views is true but you never call Fortify::loginView()?The `GET /login` route is registered, but its controller resolves the `LoginViewResponse` contract, and only `Fortify::loginView()` binds an implementation. With nothing bound, the request fails. Either bind a view in `FortifyServiceProvider::boot()` or set `'views' => false`.
- Why must an SPA with views disabled still define a password.reset route?Laravel's `ResetPassword` notification builds the link in the email from the named route `password.reset`. With views disabled Fortify no longer registers that GET route, so the application must define one, typically pointing at the SPA's reset screen, or sending the email fails.
saying these in an interview costs you the question
- Fortify ships ready-made Blade login and registration pages.
- Fortify and Sanctum are alternatives, so you pick one.
- Setting views to false disables the POST login endpoint too.
- Removing a feature from the array only hides its link in the UI.
- The Fortify stub enables email verification by default.