🎓 Top 15 Udemy Courses (80-90% Discount): My Udemy Courses - Ramesh Fadatare — All my Udemy courses are real-time and project oriented courses.
▶️ Subscribe to My YouTube Channel (178K+ subscribers): Java Guides on YouTube
▶️ For AI, ChatGPT, Web, Tech, and Generative AI, subscribe to another channel: Ramesh Fadatare on YouTube
If you’ve ever worked on a Spring Boot backend paired with a React, Angular, or Vue frontend, chances are you’ve run into that dreaded error:
Access to fetch at 'http://localhost:8080/api/data' from origin 'http://localhost:3000' 
has been blocked by CORS policy.It’s the bane of many frontend-backend integrations — CORS (Cross-Origin Resource Sharing) errors that break communication, confuse newcomers, and waste hours of debugging time.
What if fixing it didn’t require a complex setup or custom headers?
Here’s the good news: one clean CORS configuration in Spring Boot can solve all these problems — across dev, staging, and production — without exposing your app to security risks.
Let’s break it down.
What Is CORS, and Why Does It Block Your Requests?
CORS is a browser security mechanism. It prevents JavaScript on one origin (say, localhost:3000) from making requests to another origin (localhost:8080) unless the server explicitly allows it.
While it protects users from malicious cross-site attacks, it can also block legitimate communication between your frontend and backend during development or in microservice setups.
🚫 Common Symptoms of CORS Issues
- Fetch or Axios requests from frontend apps fail
 - Preflight (OPTIONS) requests return 
403 Forbidden - Authentication tokens aren’t sent or accepted
 - Errors only happen in browsers — not Postman or cURL
 
The cause? Your Spring Boot backend doesn’t know it’s supposed to allow requests from your frontend origin.
The Cleanest Fix: Global CORS Mapping in Spring Boot
The most robust and maintainable way to handle CORS in Spring Boot is by using a WebMvcConfigurer bean.
✅ Add This One Config:
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**") // Apply to all paths
                .allowedOrigins("http://localhost:3000") // Frontend origin
                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
                .allowedHeaders("*")
                .allowCredentials(true);
    }
}Why This Works
addMapping("/**"): Applies to all endpoints in your appallowedOrigins(...): Only allows requests from your frontend (can be multiple origins)allowedMethods(...): Supports all major HTTP verbsallowCredentials(true): Allows cookies or Authorization headers to be sentallowedHeaders("*"): Accepts any custom headers (likeAuthorization,Content-Type, etc.)
This configuration lives in a single place, scales well across endpoints, and can be easily extended for multiple environments or microservices.
Real-World Example: React + Spring Boot
React app making a request:
fetch("http://localhost:8080/api/products", {
  method: "GET",
  credentials: "include", // Needed if using cookies
  headers: {
    "Content-Type": "application/json"
  }
});Spring Boot backend returns:
Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Allow-Credentials: trueThe browseraccepts the response. No CORS error. Request successful.
🛡 Security Note
You might be tempted to write:
.allowedOrigins("*")That will work, but it’s not secure — especially if you’re using allowCredentials(true) (which Spring will reject if * is used).
Use explicit origins (http://localhost:3000, https://yourfrontend.com) to avoid exposing your backend to untrusted sources.
Handling Multiple Frontend Origins
For environments like dev, staging, and production:
.allowedOrigins(
    "http://localhost:3000",
    "https://staging.frontend.com",
    "https://www.frontend.com"
)Or load them from application.properties:
cors.allowed-origins=http://localhost:3000,https://frontend.comThen bind using @ConfigurationProperties or @Value in your config class.
Alternative: Controller-Level CORS (Not Recommended for Large Apps) using @CrossOrigin annotation
You can also add CORS config per controller:
@CrossOrigin(origins = "http://localhost:3000")
@RestController
@RequestMapping("/api")
public class ProductController {
    ...
}This is fine for small or isolated APIs, but becomes hard to maintain across multiple controllers and environments.
Prefer global config unless you have a specific reason to isolate CORS per controller.
Spring Security + CORS: The Common Gotcha
If you’re using Spring Security and still getting CORS errors after adding WebMvcConfigurer, the reason might be that Spring Security is overriding CORS behavior.
✅ Fix:
You must enable CORS in the security filter chain too:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .cors() // Enables CORS
            .and()
            .csrf(AbstractHttpConfigurer::disable)
            .authorizeHttpRequests()
                .anyRequest().permitAll();
        return http.build();
    }
    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowedOrigins(List.of("http://localhost:3000"));
        config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
        config.setAllowedHeaders(List.of("*"));
        config.setAllowCredentials(true);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return source;
    }
}Important:
- When using Spring Security, 
@CrossOriginandWebMvcConfigureralone won’t work. - The security filter chain must include 
cors()and aCorsConfigurationSource. 
Debugging CORS Issues
✅ Use Browser DevTools
- Open the Network tab
 - Click on the failed request
 - Check Response Headers
 - Look for missing 
Access-Control-Allow-Originor mismatched values 
✅ Use cURL with Origin Header
curl -H "Origin: http://localhost:3000" \
     -H "Access-Control-Request-Method: GET" \
     -X OPTIONS http://localhost:8080/api/products -vIf the response doesn’t include the proper CORS headers, the backend is misconfigured.
Summary: One CORS Config to Rule Them All

Conclusion
CORS is one of the most misunderstood issues in modern web development — not because it’s complex, but because the fix is often misplaced.
Spring Boot makes it easy to configure CORS the right way. With just one configuration class, it’s possible to:
- Eliminate development-time errors
 - Enable secure cross-origin requests
 - Scale from local testing to production deployments
 
No more jumping through hoops or disabling browser security. Just clean, declarative configuration.
Comments
Post a Comment
Leave Comment