skip to content

What does Spring Security's logout() DSL configure, and what happens when a user hits /logout?

level: juniorimportance: must knowfreq 58%

answer

  1. logout() DSL -> LogoutFilter
  2. default POST /logout (CSRF on)
  3. handlers then success handler
  4. clear context + invalidate session
  5. default success /login?logout

basics

~10 s

The logout() DSL wires up a LogoutFilter. When a user POSTs to /logout, the filter clears the logged-in user from the SecurityContext, invalidates the HTTP session, and redirects to a success page (default /login?logout).

solid answer

~30 s

The logout() DSL in the SecurityFilterChain registers a LogoutFilter. By default it matches POST /logout (POST because CSRF protection is on by default). When the request matches, the filter runs a chain of LogoutHandlers: SecurityContextLogoutHandler clears the SecurityContextHolder and invalidates the HttpSession; if remember-me or deleteCookies() are configured, their handlers clear those cookies. Finally a LogoutSuccessHandler decides the response — the default SimpleUrlLogoutSuccessHandler redirects to /login?logout. You customize with logoutUrl(), logoutSuccessUrl(), deleteCookies(), invalidateHttpSession(), and addLogoutHandler(). Logout is essentially 'clean up server-side auth state, then tell the client where to go'.

code

java · 13 lines
java
@Bean
SecurityFilterChain security(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
        .formLogin(Customizer.withDefaults())
        .logout(logout -> logout
            .logoutUrl("/logout")                 // POST /logout by default (CSRF on)
            .logoutSuccessUrl("/login?logout")    // where to redirect afterwards
            .invalidateHttpSession(true)          // kill the HttpSession
            .clearAuthentication(true)            // clear the SecurityContext
            .deleteCookies("JSESSIONID"));         // tidy up the cookie
    return http.build();
}

go deeper

for a junior

Know the DSL triggers a LogoutFilter, that it clears the context and session, and that default logout is POST /logout redirecting to /login?logout.

for a middle

Explain the POST-only default via CSRF, and the handler-then-success-handler pipeline.

for a senior

Discuss customizing the matcher, swapping in a REST-friendly success handler, and adding custom LogoutHandlers.

for a principal

Frame logout as server-side auth-state teardown and contrast session vs stateless semantics.

## What logout means in Spring Security Logging out is the act of destroying the server-side traces of an authenticated session so the next request is treated as anonymous. In a session-based app, the two key pieces of state are (1) the **SecurityContext** (which holds the `Authentication` object for the current user) and (2) the **HttpSession** (the servlet session, keyed by the `JSESSIONID` cookie). Logout must clear both. ## The logout() DSL Inside your `SecurityFilterChain` bean you configure logout like: ```java http.logout(logout -> logout .logoutUrl("/logout") .logoutSuccessUrl("/login?logout") .deleteCookies("JSESSIONID") .invalidateHttpSession(true)); ``` This DSL builds and registers a **`LogoutFilter`** into the filter chain. You don't create the filter yourself; the DSL does. ## What the LogoutFilter does `LogoutFilter` is a normal servlet filter. On every request it asks its **`RequestMatcher`** (the `logoutRequestMatcher`) whether this request is a logout request. - **Default URL:** `/logout`. - **Default HTTP method:** because Spring Security enables **CSRF protection by default**, the default logout matcher requires **POST** `/logout`. This prevents a malicious `<img src="/logout">` from logging users out via a GET. If you disable CSRF, logout will also accept GET. If the request matches, the filter: 1. Invokes each configured **`LogoutHandler`** in order (via a `CompositeLogoutHandler`). The built-in `SecurityContextLogoutHandler` clears the `SecurityContextHolder` and invalidates the `HttpSession`. 2. Delegates to a **`LogoutSuccessHandler`** to produce the response (default: redirect to `/login?logout`). If the request does **not** match, the filter simply calls `chain.doFilter(...)` and does nothing. ## Common customizations - `logoutUrl("/perform-logout")` — change the trigger URL. - `logoutRequestMatcher(...)` — full control (e.g. allow GET logout). - `logoutSuccessUrl("/goodbye")` — where to redirect after logout. - `logoutSuccessHandler(...)` — replace the redirect entirely (e.g. return HTTP 200 for a SPA/REST client). Note: setting a handler **overrides** `logoutSuccessUrl`. - `deleteCookies("JSESSIONID", "remember-me")` — registers a `CookieClearingLogoutHandler`. - `invalidateHttpSession(true|false)` — toggles session invalidation on `SecurityContextLogoutHandler`. - `addLogoutHandler(customHandler)` — plug in extra cleanup (audit, token denylist, etc.). ## Gotchas - A **GET `/logout` link that does nothing** is the classic bug: with default (CSRF-on) config, only POST logs out. Use a form that POSTs, or configure a GET matcher. - Invalidating the session throws away **all** session attributes, not just security ones. - Deleting the `JSESSIONID` cookie via `deleteCookies` is cosmetic in most setups — invalidating the session server-side is what actually ends it — but it keeps the browser tidy. - For **stateless JWT** APIs there is no session to invalidate; logout on the server does little unless you maintain a token denylist (see the principal-level question).

  • Why does a plain <a href="/logout">Logout</a> link often fail to log the user out?
    With Spring Security's default configuration CSRF is enabled, so the logout matcher only accepts POST. A GET link doesn't match, the LogoutFilter passes it through, and nothing happens. Use a form that POSTs to /logout (with the CSRF token), or explicitly configure a GET logout matcher.
  • Where does the user end up by default after logging out?
    The default LogoutSuccessHandler (SimpleUrlLogoutSuccessHandler) redirects to /login?logout. You can change this with logoutSuccessUrl(...) or replace it entirely with logoutSuccessHandler(...).

saying these in an interview costs you the question

  • Thinking GET /logout works by default (it needs POST when CSRF is on).
  • Believing logout deletes the user from the database or revokes credentials.
  • Claiming you must manually instantiate and register the LogoutFilter.

context