How do you handle CORS in a Spring Boot application?

7 minintermediatecorsspring-mvcsecurity

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.