When should you use authorizeHttpRequests().permitAll() versus WebSecurityCustomizer web.ignoring(), and how are HttpSecurity, WebSecurity, and FilterChainProxy related?
answer
- permitAll = runs chain, skips authz check
- ignoring() = skips whole FilterChainProxy
- WebSecurity builds FilterChainProxy; HttpSecurity builds one chain
- ignoring drops headers/CSRF/context
- Prefer permitAll; ignoring only for static assets
basics
~20 spermitAll() lets a request through but still runs the security filter chain (CSRF, headers, etc.). web.ignoring() (WebSecurityCustomizer) tells Spring to skip the entire chain for those paths. Prefer permitAll(); reserve ignoring() for truly static, security-irrelevant resources.
solid answer
~40 sThere are two layers. WebSecurity builds the top-level FilterChainProxy and, via a WebSecurityCustomizer, lets you web.ignoring().requestMatchers(...) to exclude paths so no SecurityFilterChain runs for them at all — no CSRF token, no security headers, no context. HttpSecurity builds an individual SecurityFilterChain and permitAll() authorizes a request while still running every filter in that chain. Prefer permitAll() because you keep protective headers, HTTPS redirect, and CSRF plumbing; use ignoring() only for high-volume, genuinely static assets where you accept the filters won't run and where the endpoint has no sensitive behavior. Modern guidance strongly favors permitAll() (or a dedicated chain with securityMatcher and minimal filters) over ignoring(), which the framework even warns about because it silently disables all protections for those paths.
code
java · 19 lines@Configuration
@EnableWebSecurity
public class SecurityConfig {
// Total bypass: no filters, no headers, no CSRF — static assets only
@Bean
WebSecurityCustomizer webSecurityCustomizer() {
return web -> web.ignoring().requestMatchers("/css/**", "/js/**", "/images/**");
}
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(auth -> auth
// Runs the full chain (headers, CSRF) but allows access
.requestMatchers("/", "/login", "/actuator/health").permitAll()
.anyRequest().authenticated());
return http.build();
}
}go deeper
May not know WebSecurityCustomizer exists.
Knows permitAll runs the chain and ignoring skips it, at a high level.
Articulates the header/CSRF/context consequences and prefers permitAll.
Maps WebSecurity/HttpSecurity/FilterChainProxy roles, weighs ignoring vs dedicated stripped chain, and sets team policy.
**Three objects, three roles.** - **`FilterChainProxy`** — the single Servlet `Filter` (bean name `springSecurityFilterChain`) that Spring inserts into the container's filter chain. It owns *all* your `SecurityFilterChain`s and dispatches each request to the first matching one. - **`WebSecurity`** — the builder that *produces* the `FilterChainProxy`. You customize it by declaring a **`WebSecurityCustomizer`** bean. Its most-used feature is `web.ignoring()`. - **`HttpSecurity`** — the builder that produces one **`SecurityFilterChain`** (matcher + filters). This is where `authorizeHttpRequests`, `csrf`, `formLogin`, etc. live. **The two ways to "let something through".** 1. **`authorizeHttpRequests(a -> a.requestMatchers("/public/**").permitAll())`** — the request **still enters** the security filter chain. All filters run: CSRF token handling, `SecurityContext` setup, security response headers (`X-Content-Type-Options`, `Cache-Control`, `Strict-Transport-Security`, etc.), channel/HTTPS redirect, exception translation. Only the *authorization check* is short-circuited to "allow". The principal may be anonymous. 2. **`WebSecurityCustomizer` → `web.ignoring().requestMatchers("/assets/**")`** — the path is **excluded from the FilterChainProxy entirely**. No `SecurityFilterChain` runs. Consequences: no security headers, no CSRF token generation, no `SecurityContext`, no authorization possible at all. It is a total bypass. ```java @Bean WebSecurityCustomizer webSecurityCustomizer() { // Skip the whole chain for static, non-sensitive resources only return web -> web.ignoring().requestMatchers("/css/**", "/js/**", "/images/**"); } ``` **Why prefer `permitAll()`.** - **Security headers still apply**, so even public pages get clickjacking/HSTS/content-type protections. - **CSRF plumbing stays intact**, which matters if a "public" page later posts a form or issues the CSRF cookie other pages need. - **Auditability**: the request still flows through the observable, logged chain. Spring Security's own reference logs a **warning** when `web.ignoring()` is used against endpoints that could otherwise be `permitAll`, precisely because it silently drops protections. The framework's guidance: use `ignoring()` only for **static resources** that are genuinely security-irrelevant *and* where the filter overhead is a measured concern. **A third, preferred alternative for high-volume static content:** a **dedicated `SecurityFilterChain`** scoped with `securityMatcher("/assets/**")` that disables the expensive filters explicitly (e.g. `requestCache(disable)`, `securityContext(disable)`, `sessionManagement(disable)`) while *keeping* the ones you want. This gives fine-grained control that `ignoring()` cannot, without a blanket bypass. **Edge cases & gotchas.** - `ignoring()` paths cannot use method security or any authorization — there is no `SecurityContext`. - Because ignored requests skip the chain, they also skip **CORS** handling configured on `HttpSecurity` — surprising for API assets. - Ordering: `web.ignoring()` effectively takes precedence because those paths never reach the chains at all. - With multiple chains, `permitAll()` in a chain still requires that the chain's `securityMatcher` claims the request. **When to use which.** - Login page, public API docs, health endpoints you still want headers/CSRF on → **`permitAll()`**. - Fingerprinted static assets under heavy load, no sensitive behavior → **`ignoring()`** or a dedicated stripped-down chain. - Default answer in interviews: **prefer `permitAll()`; treat `ignoring()` as a deliberate, narrow performance exception.**
- Why can web.ignoring() be dangerous compared to permitAll()?It removes the path from the FilterChainProxy entirely, so no security headers, no CSRF token, no SecurityContext, and no possibility of authorization run. If the endpoint ever gains sensitive behavior it is silently unprotected — which is why Spring warns against it for non-static paths.
- What is the relationship between WebSecurity and HttpSecurity?WebSecurity is the outer builder that assembles the FilterChainProxy and can globally ignore paths. HttpSecurity is the inner builder that configures a single SecurityFilterChain (its matcher and filters). WebSecurity contains/precedes the individual HttpSecurity-built chains.
- How would you serve heavy static assets without the ignoring() downsides?Define a dedicated SecurityFilterChain with securityMatcher("/assets/**") that disables only the costly filters (request cache, session, security context) while keeping headers, giving controlled performance without a blanket bypass.
saying these in an interview costs you the question
- Claiming permitAll and ignoring() are equivalent
- Thinking permitAll skips the filters
- Using web.ignoring() for API or authenticated-adjacent endpoints
- Believing ignored paths still get security headers or CSRF tokens