How do you handle CORS in a Spring Boot application?
Quick Answer
CORS (Cross-Origin Resource Sharing) rules are typically configured either per-controller/method with @CrossOrigin, or globally via a WebMvcConfigurer's addCorsMappings() method (or a CorsFilter bean), specifying which origins, HTTP methods, and headers are allowed to make cross-origin requests to the API. This is necessary because browsers block cross-origin JavaScript requests by default unless the server explicitly opts in via the appropriate Access-Control-* response headers, which Spring generates automatically once CORS is configured.
Detailed Answer
CORS (Cross-Origin Resource Sharing) is a browser-enforced security mechanism: by default, a webpage served from one origin (https://app.example.com) cannot make a JavaScript-initiated request to a different origin's API (https://api.example.com) unless that API explicitly opts in via specific Access-Control-* response headers. Spring Boot provides a few ways to configure this opt-in.
1. Per-controller/method with @CrossOrigin:
@RestController
@CrossOrigin(origins = "https://app.example.com")
class OrderController {
@GetMapping("/orders")
List<Order> listOrders() { ... } // this endpoint allows cross-origin requests from that origin
}
2. Globally, via a WebMvcConfigurer (the more common approach for an entire API):
@Configuration
class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("https://app.example.com")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.allowCredentials(true);
}
}
3. Via a CorsFilter bean — useful when CORS needs to be applied even earlier, e.g., ahead of Spring Security's filter chain, since Spring Security's own filters can otherwise reject a request (including its CORS preflight OPTIONS request) before it ever reaches Spring MVC's CORS handling.
Important interaction with Spring Security: if Spring Security is on the classpath, it has its own CORS configuration hook (http.cors(Customizer.withDefaults()) in a SecurityFilterChain), which needs to be wired to the same CorsConfigurationSource — configuring CORS only at the Spring MVC level, without also enabling it in Spring Security's filter chain, is a common source of confusing "CORS is configured but requests still get blocked" issues, since Security's filters run before Spring MVC's own CORS handling would ever be reached.
Common mistakes: using allowedOrigins("*") together with allowCredentials(true) is invalid per the CORS spec and browsers will reject it — a wildcard origin cannot be combined with credentialed requests; you must list explicit origins (or use allowedOriginPatterns for pattern-based matching) if credentials are involved.