Separate Symfony Stateless Routes from Remember-Me Login

A route declared stateless: true promises that handling it will not use the session. If the same security path also runs remember-me or session-backed browser authentication, Symfony can detect session access and throw UnexpectedSessionUsageException. Separate the security boundaries.

Last updated: November 11, 2026.

# config/packages/security.yaml
security:
  firewalls:
    api:
      pattern: ^/api/
      stateless: true
      access_token: ~

    main:
      lazy: true
      provider: app_user_provider
      form_login: ~
      remember_me:
        secret: '%kernel.secret%'
        lifetime: 604800

Order the API firewall before the broader browser firewall. API clients authenticate on every request with a stateless mechanism, while browser pages may continue using form login, remember-me, and the session.

Make the route promise match the firewall

Symfony’s routing configuration reference describes the route stateless option. In debug mode, unexpected session use can raise an exception; in other environments it may be logged. The option is a diagnostic contract, not a command that magically makes session-aware authentication stateless.

The security configuration reference provides a firewall-level stateless setting. Give API routes a distinct path or host and match them with that firewall. Symfony uses the first matching firewall, so place the narrow ^/api/ rule before main and confirm the match with the security profiler. Do not enable form login or remember-me on the stateless firewall.

Choose authentication for each client

Symfony’s remember-me documentation is designed for browser authentication that persists through a cookie. It belongs with the browser firewall, where session use is allowed. A stateless API should instead validate its access token, signed request, or other credential on every request. Give the API its own entry point and access-control rules so failed authentication also returns an API response rather than a browser login redirect.

Do not hide the exception with a broad catch or start a session conditionally. That makes the route’s behavior depend on which cookies the browser happened to send. Add functional tests for an anonymous request, a valid API credential, and a request carrying the browser remember-me cookie. Each API test should complete without creating or reading a session.

Related Web Cheat Sheet guides

Sergey Kornilov

Sergey Kornilov