CORS done right
Configure cross-origin access in the security chain so preflight requests stop failing.
Open this lesson in the learning hubKey points
- CORS is a browser rule, not server protection. It decides whether JS on one origin may read a response from another.
- Anything non-simple triggers a preflight
OPTIONS. It carries no credentials, so it must pass before authentication. - Configure it in the chain:
cors(Customizer.withDefaults())picks up yourCorsConfigurationSourcebean. @CrossOriginon a controller is too late if the chain rejects the preflight first. Prefer one central config.allowCredentials(true)forbids*. List exact origins, or useallowedOriginPatternsfor wildcards.
Example
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration cfg = new CorsConfiguration();
cfg.setAllowedOrigins(List.of("https://app.example.com"));
cfg.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
cfg.setAllowedHeaders(List.of("Authorization", "Content-Type"));
cfg.setAllowCredentials(true);
cfg.setMaxAge(Duration.ofMinutes(30)); // cache the preflight
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/api/**", cfg);
return source;
}
// in the SecurityFilterChain
http.cors(Customizer.withDefaults());
CORS belongs in the security chain — otherwise the preflight dies before your rules run.
This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Spring Security course, and every lesson in it is listed on the Spring Security contents page.