Spring Security: permitAll() vs web.ignoring() (Spring Boot 3)
You want to make an endpoint public. Maybe it’s your login page, maybe it’s a CSS file. You have two choices: permitAll() or web.ignoring().
They look the same. They act the same (the user sees the page). But under the hood, they are radically different.
At www.codegigs.app, I constantly see developers using web.ignoring() on their login endpoints to “fix” 403 errors. This is dangerous. It turns off all security protections—CSRF, Headers, everything.
Here is exactly when to use each, and why getting it wrong leaves you vulnerable.
The Safe Choice: permitAll()
Think of permitAll() like a security guard who checks your ID, sees you’re a guest, and waves you through. You are still “inside” the security system.
When you use permitAll(), the request still passes through the Security Filter Chain. This means:
- CSRF protection is active (crucial for login forms).
- Security Headers (XSS, Frame Options) are added.
- The
SecurityContextis available (so you can check if a user is logged in, even on a public page).
// SecurityConfig.java
// Spring Boot 3.2+
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
// Use permitAll for any endpoint that has logic or forms
.requestMatchers("/", "/login", "/register", "/api/public/**").permitAll()
.anyRequest().authenticated()
)
// ... rest of config
;
return http.build();
}
If you have a controller, use permitAll(). Always.
The Performance Choice: web.ignoring()
Now think of web.ignoring() like a side door that bypasses the security guard entirely. The request never enters the Spring Security filter chain.
It’s faster because it skips the filters. But it’s “unsafe” because Spring Security is completely blind to these requests. No CSRF. No Headers. No UserDetails.
Use this only for static resources (CSS, JS, Images).
// SecurityConfig.java
// Note: This returns WebSecurityCustomizer, NOT SecurityFilterChain
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.ignoring()
.requestMatchers("/css/**", "/js/**", "/images/**", "/favicon.ico");
}
web.ignoring() for your /login endpoint. If you do, you lose CSRF protection on your login form, making your users vulnerable to login-forgery attacks.
Why Does It Matter?
I debugged an app last month where the developer used web.ignoring() on a public “Contact Us” form because they were getting CSRF errors.
By ignoring the endpoint, they bypassed the CSRF check. The errors stopped. Great, right?
Wrong. They effectively turned off the firewall because the door was stuck. Any malicious site could now submit that form on behalf of a user. The correct fix was to include the CSRF token, not disable the security.
Summary: The Decision Matrix
- Is it a static file (CSS/JS/PNG)? Use
web.ignoring(). - Is it a Controller or API endpoint? Use
permitAll(). - Is it the Login page? Use
permitAll(). - Is it H2 Console? Use
web.ignoring()(usually easiest, though less secure).
Confused by the Filter Chain?
We visualize the entire request flow in our architecture module.
Stop guessing which filter runs when.
Now that you know how to let people in, learn how to keep the wrong people out by securing your methods with annotations.