Cross-Origin Resource Sharing (CORS) is a crucial security mechanism that allows or restricts resources requested from one domain to another domain. In Spring Boot applications, proper CORS configuration is essential for frontend applications to communicate with your backend API.
What is CORS?
CORS is a browser security feature that blocks requests from one origin to another unless the server explicitly allows it. This becomes important when your frontend (e.g., React app on http://localhost:3000) needs to access your backend API (e.g., Spring Boot on http://localhost:8080).
CORS Configuration Methods in Spring Boot
Method 1: Global CORS Configuration
Create a global CORS configuration that applies to all endpoints in your application.
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; import java.util.Arrays; @Configuration public class CorsConfig { @Bean public CorsFilter corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration config = new CorsConfiguration(); // Allow credentials config.setAllowCredentials(true); // Allow specific origins config.setAllowedOriginPatterns(Arrays.asList( "http://localhost:3000", "http://localhost:4200", "https://mydomain.com" )); // Allow specific headers config.setAllowedHeaders(Arrays.asList( "Origin", "Content-Type", "Accept", "Authorization", "X-Requested-With" )); // Allow specific methods config.setAllowedMethods(Arrays.asList( "GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH" )); // Expose specific headers to the client config.setExposedHeaders(Arrays.asList( "Authorization", "Content-Disposition" )); // How long the response to preflight request can be cached (in seconds) config.setMaxAge(3600L); source.registerCorsConfiguration("/**", config); return new CorsFilter(source); } } Method 2: Using WebMvcConfigurer (for Spring MVC)
import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") .allowedOriginPatterns("http://localhost:3000", "https://*.mydomain.com") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .allowCredentials(true) .maxAge(3600); registry.addMapping("/public/**") .allowedOriginPatterns("*") .allowedMethods("GET") .allowedHeaders("*") .maxAge(1800); } } Method 3: Using WebFluxConfigurer (for Spring WebFlux)
import org.springframework.context.annotation.Configuration; import org.springframework.web.reactive.config.CorsRegistry; import org.springframework.web.reactive.config.WebFluxConfigurer; @Configuration public class WebFluxConfig implements WebFluxConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("http://localhost:3000") .allowedMethods("*") .allowedHeaders("*") .allowCredentials(true) .maxAge(3600); } } Method 4: Controller-Level CORS Configuration
Apply CORS configuration to specific controllers or methods.
import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/users") @CrossOrigin(origins = "http://localhost:3000", maxAge = 3600, allowCredentials = "true") public class UserController { @GetMapping @CrossOrigin(origins = {"http://localhost:3000", "https://admin.mydomain.com"}) public List<User> getUsers() { // Implementation return userService.getAllUsers(); } @PostMapping public User createUser(@RequestBody User user) { // Implementation return userService.createUser(user); } } Method 5: Method-Level CORS with @CrossOrigin
@RestController @RequestMapping("/api/products") public class ProductController { @GetMapping @CrossOrigin(origins = "*") // Allow all origins for this endpoint public List<Product> getAllProducts() { return productService.getAllProducts(); } @PostMapping @CrossOrigin(origins = "http://localhost:3000", methods = RequestMethod.POST, allowedHeaders = {"Content-Type", "Authorization"}) public Product createProduct(@RequestBody Product product) { return productService.createProduct(product); } } Application Properties Configuration
You can also configure CORS through application.properties or application.yml:
application.properties
# CORS configuration app.cors.allowed-origins=http://localhost:3000,https://mydomain.com app.cors.allowed-methods=GET,POST,PUT,DELETE,OPTIONS app.cors.allowed-headers=* app.cors.allow-credentials=true app.cors.max-age=3600
application.yml
app: cors: allowed-origins: - "http://localhost:3000" - "https://mydomain.com" allowed-methods: GET,POST,PUT,DELETE,OPTIONS allowed-headers: "*" allow-credentials: true max-age: 3600
Security-Specific CORS Configuration
When using Spring Security, configure CORS through security configuration:
import org.springframework.context.annotation.Bean; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.web.SecurityFilterChain; import org.springframework.web.cors.CorsConfigurationSource; @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .cors(cors -> cors.configurationSource(corsConfigurationSource())) .csrf(csrf -> csrf.disable()) // Often disabled for APIs .authorizeHttpRequests(authz -> authz .requestMatchers("/api/public/**").permitAll() .requestMatchers("/api/admin/**").hasRole("ADMIN") .anyRequest().authenticated() ); return http.build(); } @Bean public CorsConfigurationSource corsConfigurationSource() { // Return your CorsConfigurationSource implementation return new UrlBasedCorsConfigurationSource(); } } Testing CORS Configuration
Create a test to verify your CORS configuration:
import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.web.servlet.MockMvc; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.options; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @SpringBootTest @AutoConfigureMockMvc public class CorsTest { @Autowired private MockMvc mockMvc; @Test public void testCorsHeaders() throws Exception { mockMvc.perform(options("/api/users") .header("Origin", "http://localhost:3000") .header("Access-Control-Request-Method", "GET")) .andExpect(status().isOk()) .andExpect(header().exists("Access-Control-Allow-Origin")) .andExpect(header().string("Access-Control-Allow-Origin", "http://localhost:3000")); } } Common CORS Issues and Solutions
Issue 1: Preflight Request Failing
Solution: Ensure OPTIONS method is allowed and proper headers are set.
Issue 2: Credentials Not Working
Solution: Set allowCredentials(true) and use specific origins (not wildcard *).
Issue 3: Headers Not Exposed
Solution: Use setExposedHeaders() to expose custom headers to the client.
Issue 4: Multiple CORS Configurations
Solution: Be aware that method-level configurations override class-level, which override global configurations.
Best Practices
- Be Specific with Origins: Avoid using
*for origins when credentials are required. - Limit Exposed Headers: Only expose necessary headers to enhance security.
- Use Appropriate Max Age: Balance between performance and security.
- Environment-Specific Configuration: Use different CORS settings for development, testing, and production.
- Test Thoroughly: Verify CORS behavior across different browsers and scenarios.
This comprehensive CORS configuration guide ensures your Spring Boot application can securely handle cross-origin requests while maintaining proper security standards.