CORS
CorsFilter
Spring Security
Error Handling
Web Development

Cors Error when using CorsFilter and spring security

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

CORS errors in a Spring application often happen not because CORS is completely missing, but because Spring Security and the CORS configuration disagree about who should handle preflight and cross-origin headers. The clean solution is usually to let Spring Security integrate with a single clear CORS configuration source instead of layering several partial fixes on top of each other.

Why CORS Fails with Spring Security

A browser sends preflight OPTIONS requests before certain cross-origin calls. If that request is blocked by security before the CORS headers are added, the browser reports a CORS failure even though the real backend problem is filter-chain ordering or missing security integration.

Typical symptoms include:

  • '403 Forbidden on OPTIONS'
  • missing Access-Control-Allow-Origin
  • preflight succeeds in Postman but fails in the browser
  • duplicate or conflicting CORS headers

Prefer Security-Integrated CORS Configuration

In modern Spring Security setups, the usual pattern is to register a CorsConfigurationSource and enable http.cors() in the security chain.

java
1import java.util.List;
2import org.springframework.context.annotation.Bean;
3import org.springframework.context.annotation.Configuration;
4import org.springframework.security.config.Customizer;
5import org.springframework.security.config.annotation.web.builders.HttpSecurity;
6import org.springframework.security.web.SecurityFilterChain;
7import org.springframework.web.cors.CorsConfiguration;
8import org.springframework.web.cors.CorsConfigurationSource;
9import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
10
11@Configuration
12public class SecurityConfig {
13
14    @Bean
15    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
16        http
17            .cors(Customizer.withDefaults())
18            .csrf(csrf -> csrf.disable())
19            .authorizeHttpRequests(auth -> auth.anyRequest().authenticated());
20
21        return http.build();
22    }
23
24    @Bean
25    CorsConfigurationSource corsConfigurationSource() {
26        CorsConfiguration config = new CorsConfiguration();
27        config.setAllowedOrigins(List.of("http://localhost:3000"));
28        config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
29        config.setAllowedHeaders(List.of("*"));
30        config.setAllowCredentials(true);
31
32        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
33        source.registerCorsConfiguration("/**", config);
34        return source;
35    }
36}

This approach lets Spring Security participate correctly instead of accidentally rejecting requests before CORS handling runs.

Avoid Double Configuration Unless You Mean It

A common anti-pattern is combining:

  • a standalone CorsFilter
  • controller-level @CrossOrigin
  • custom headers from another filter
  • Spring Security CORS integration

all at the same time.

That can produce confusing behavior because different parts of the stack may add different headers or run in different order.

Usually one consistent configuration path is better than several overlapping ones.

Preflight Requests Must Be Allowed

If the browser sends OPTIONS and the backend blocks it, the real application request never happens.

That is why your security rules must allow the preflight path to be processed correctly. Sometimes the CORS configuration is fine, but authorization rules are still too strict.

The security chain and CORS chain have to cooperate.

allowedOrigins and Credentials Matter

If allowCredentials(true) is used, a wildcard origin such as * is not appropriate in the typical credentialed browser flow. That mismatch often leads to confusing browser-side failures.

So if cookies or authorization headers are involved, use explicit allowed origins instead of a blanket wildcard.

How to Debug It

A useful debugging checklist is:

  1. inspect the browser Network tab
  2. look at the OPTIONS preflight response
  3. check whether CORS headers are present there
  4. confirm whether Spring Security returned 401 or 403 before CORS handling completed
  5. reduce configuration to one CORS mechanism instead of many

The browser error message alone often hides which backend response actually caused the failure.

Common Pitfalls

The most common mistake is adding a CorsFilter but forgetting that Spring Security can still block the request before the browser sees the right headers.

Another issue is configuring CORS in multiple places and then getting conflicting behavior that is hard to reason about.

People also allow credentials while using overly broad or incompatible origin settings.

Finally, do not debug CORS only with Postman. CORS is enforced by browsers, so the browser network trace is the most relevant evidence.

Summary

  • CORS problems with Spring Security are often filter-chain coordination problems.
  • A CorsConfigurationSource plus http.cors() is usually the cleanest setup.
  • Preflight OPTIONS requests must be allowed to pass correctly.
  • Avoid overlapping CORS configuration in several places unless you need it intentionally.
  • Debug in the browser network tab, not only with non-browser clients.

Course illustration
Course illustration

All Rights Reserved.