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.