Course Content
Spring Security Series
0/28
Spring Security

Spring Security `permitAll()` vs. `web.ignoring()`: The Definitive Guide to Secure & Performant Applications

Navigating the powerful landscape of Spring Security can sometimes feel like deciphering a secret code. Among the most common points of confusion for developers, both new and experienced, is the subtle but critical difference between `.permitAll()` and `web.ignoring()`. Both seem to achieve a similar goal: making certain endpoints publicly accessible. However, how they achieve this goal has profound implications for your application’s security posture and performance. Misunderstanding this distinction can lead to subtle vulnerabilities or unnecessary processing overhead. This guide will demystify these two configurations, providing a definitive breakdown of what they do, when to use each, and how to make the right choice for a secure and performant application.

Understanding the Spring Security Filter Chain

Before we can compare `permitAll()` and `web.ignoring()`, we must first understand the heart of Spring Security: the Security Filter Chain. Imagine your application is an exclusive venue. Before any request (a guest) can reach its destination (a specific controller), it must pass through a line of security checkpoints. This line of checkpoints is the Security Filter Chain, technically managed by a bean called the `FilterChainProxy`.

Each filter in this chain has a specific responsibility. One filter might check for a CSRF token to prevent cross-site request forgery attacks. Another handles basic authentication. A different one establishes the `SecurityContext`, which holds information about the currently logged-in user. Yet another is responsible for session management. These filters work in concert to enforce your security rules. The key takeaway is this: by default, every single request that comes into your application is processed by this entire chain of filters before it ever reaches your business logic. This comprehensive, defense-in-depth approach is what makes Spring Security so robust.

Deep Dive: `http.authorizeHttpRequests().requestMatchers(…).permitAll()`

What Does `permitAll()` Actually Do?

The `permitAll()` method is an authorization rule. When you configure an endpoint with `permitAll()`, you are telling Spring Security’s authorization mechanism: “For this specific URL, you do not need to check for any specific roles or authorities. Allow any request, whether it comes from an authenticated user or a completely anonymous guest, to proceed.”

The most crucial point to understand is that the request still goes through the entire Spring Security filter chain. This is not a bypass. All the standard security filters are applied. The `SecurityContextHolder` is populated (perhaps with an `AnonymousAuthenticationToken`), CSRF protection is engaged, security headers are added, and session management is active. `permitAll()` only affects the final authorization decision, effectively disabling the need for a specific role or permission for that particular path. It’s like a security guard checking your ID, noting that you’re a “guest,” and then waving you through to a public area, while still being watchful.

Code Example: Using `permitAll()`

In modern Spring Security (Spring Boot 3+), configuration is done using `SecurityFilterChain` beans. Here’s how you would use `permitAll()` to make your homepage, about page, and login endpoint publicly accessible:

// In your @Configuration class

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests(authorize -> authorize
            // Allow unauthenticated access to these specific paths
            .requestMatchers(“/”, “/home”, “/about”, “/login”).permitAll()
            // All other requests must be authenticated
            .anyRequest().authenticated()
        )
        .formLogin(formLogin -> formLogin
            .loginPage(“/login”) // Custom login page
            .permitAll() // The login page itself must be permitted
        );

    return http.build();
}

When to Use `permitAll()`

  • Publicly Accessible Pages: Use it for pages that anyone should be able to see, such as your homepage, “About Us,” “Contact,” or a product catalog. These pages still benefit from security headers and other protections offered by the filter chain.
  • Login and Registration Endpoints: Your `/login` and `/register` endpoints must be accessible to unauthenticated users. They also need to be processed by the filter chain to handle the form submission, perform the authentication, and protect against attacks like CSRF. `permitAll()` is perfect for this.
  • Conditionally Rendered Content: Consider a homepage that displays “Welcome, Guest!” to anonymous users but “Welcome back, Jane!” to authenticated users. This is only possible because the request goes through the filter chain, which populates the `SecurityContext`, allowing your controller to check if a user is authenticated.

Deep Dive: `web.ignoring().requestMatchers(…)`

What Does `web.ignoring()` Actually Do?

In stark contrast to `permitAll()`, `web.ignoring()` operates at a completely different level. It is not an authorization rule; it is a global configuration that instructs Spring Security to completely bypass the security filter chain for the specified URL patterns. It’s like having a separate, unguarded entrance to your venue specifically for delivery trucks carrying static supplies. They never interact with the main security line.

When a request matches an ignored path, it’s as if Spring Security doesn’t exist for that request. No `SecurityContext` is established. No session is created or looked up. No CSRF protection is applied. No security headers (like `X-XSS-Protection` or `Strict-Transport-Security`) are added by Spring Security. The request goes straight from the servlet container to Spring’s `DispatcherServlet`. This offers a slight performance benefit, as the overhead of processing a dozen security filters is eliminated.

Code Example: Using `web.ignoring()`

This configuration is typically done via a `WebSecurityCustomizer` bean. It’s important to note this is separate from your main `HttpSecurity` configuration chain.

// In your @Configuration class

@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
    // Instruct Spring Security to completely ignore requests to static resources.
    return (web) -> web.ignoring().requestMatchers(“/css/**”, “/js/**”, “/images/**”, “/favicon.ico”);
}

When to Use `web.ignoring()`

  • Static Resources: This is the primary and most recommended use case. Your CSS stylesheets, JavaScript files, images, fonts, and other static assets have no need for a security context or session management. Applying the filter chain to them is pure, unnecessary overhead. Ignoring them is a safe and effective performance optimization.
  • Public, Non-sensitive Health Checks: Infrastructure tools often poll health check endpoints like `/actuator/health`. These requests are frequent and need to be fast. Since they contain no sensitive data and require no user context, ignoring them can be appropriate.
  • Public API Documentation: If you serve a completely static Swagger UI or other API documentation, ignoring those paths can be a valid choice, as they are just a collection of HTML, CSS, and JS files.

The Key Differences: A Head-to-Head Comparison

Let’s summarize the critical distinctions in a clear, side-by-side format to solidify your understanding.

  • Mechanism: `permitAll()` is an authorization rule applied within the filter chain. `web.ignoring()` is a global setting that bypasses the filter chain entirely.
  • Security Context: With `permitAll()`, the `SecurityContextHolder` is populated. You can check for authentication and retrieve user details if they exist. With `web.ignoring()`, the `SecurityContextHolder` remains empty. Your application has no knowledge of security for that request.
  • Security Features: `permitAll()` retains all other security features like CSRF protection, session management, and security header injection. `web.ignoring()` disables all of these features for the matched paths.
  • Performance: `web.ignoring()` provides a minor performance improvement by avoiding the cost of executing the filter chain. `permitAll()` incurs the standard processing overhead of the filters.
  • Use Case: `permitAll()` is for dynamic endpoints that need to be publicly accessible. `web.ignoring()` is for static resources that have no security logic whatsoever.

The Verdict: Which One Should You Use and When?

Here is the definitive recommendation: For almost all dynamic endpoints, even public ones, you should prefer `permitAll()`. Use `web.ignoring()` sparingly and only for truly static, non-sensitive resources.

The reasoning is rooted in the principle of “secure by default.” By keeping your application endpoints within the protection of the security filter chain using `permitAll()`, you prevent accidental vulnerabilities. Imagine you have an `/about` page that you initially decide to ignore for performance. Later, a developer adds a “Contact Us” form to that page. Because the path is ignored, this new form submission would have no CSRF protection, making it an easy target for attackers. Had you used `permitAll()`, Spring Security’s CSRF protection would have been active from the start, mitigating the risk without any code changes.

A simple and effective rule of thumb is:

  1. If the request is for a static asset (CSS, JavaScript, images, fonts), use `web.ignoring()`.
  2. If the request is handled by a Spring MVC controller (i.e., your own application code), always use `permitAll()`.

A Note on Deprecation and Modern Configuration

If you’re coming from an older Spring Security background, you may be familiar with extending the `WebSecurityConfigurerAdapter` class. This class has been deprecated since Spring Security 5.7.0. The modern approach, as shown in the code examples above, is to define your security rules as component beans (`@Bean`). The `HttpSecurity` rules are defined in a `SecurityFilterChain` bean, and the `ignoring()` rules are defined in a separate `WebSecurityCustomizer` bean. This component-based style promotes better separation of concerns and aligns with the broader Spring Boot 3 philosophy.

Conclusion: Security First, Performance Second

The choice between `permitAll()` and `web.ignoring()` is not merely a matter of preference; it’s a fundamental decision about your application’s security architecture. It represents the choice between applying a lenient security rule versus bypassing security entirely. While the minor performance gain from `web.ignoring()` might seem tempting for more than just static assets, the robust, built-in protections afforded by the security filter chain are invaluable.

By defaulting to `permitAll()` for your controllers and reserving `web.ignoring()` exclusively for static resources, you create a security posture that is strong, explicit, and less prone to accidental misconfiguration. In the world of application security, an ounce of prevention is worth a pound of cure, and keeping your requests inside the filter chain is the best prevention you can get.

Scroll to Top