An OPTIONS preflight lacks ordinary credentials; configure a bounded CORS policy in the security chain before protected requests reach authorization.
Spring Security CORS preflight: evaluate origin before authentication
Preflight is not the business request
A browser sends OPTIONS with an Origin and requested method before some cross-origin API calls. That preflight may have no session cookie or bearer token. Rejecting it for missing authentication prevents the browser from making a later valid POST. Spring Security can process CORS before authorization when a suitable configuration source is wired. The browser boundary] still does not replace server-side authentication.
Name allowed callers precisely
Accept one configured web origin, the required methods, and the exact headers used by the client. A wildcard origin with credentialed requests is a dangerous mismatch and may be rejected by browser rules. The example uses an environment-provided origin and does not permit credentials by default; a cookie-based application needs a separate, reviewed CSRF and cookie policy. Session CSRF] and bearer-token APIs should not be mixed casually.
Probe both requests
Send an OPTIONS request with a permitted Origin, Access-Control-Request-Method POST and Access-Control-Request-Headers Authorization. Assert the response permits the browser to continue without authenticating the preflight. Then send the actual POST without a valid token and require denial; a successful preflight grants no write access. Repeat with an unapproved origin and with multiple filter chains], since the selected chain controls the CORS policy.
Implementation contract
@Bean
UrlBasedCorsConfigurationSource apiCors(
@Value("${app.web-origin}") String webOrigin) {
CorsConfiguration policy = new CorsConfiguration();
policy.setAllowedOrigins(List.of(webOrigin));
policy.setAllowedMethods(List.of("GET", "POST", "OPTIONS"));
policy.setAllowedHeaders(List.of("Authorization", "Content-Type"));
UrlBasedCorsConfigurationSource source =
new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/api/**", policy);
return source;
}
@Bean
SecurityFilterChain apiChain(HttpSecurity http) throws Exception {
http.securityMatcher("/api/**")
.cors(Customizer.withDefaults())
.authorizeHttpRequests(rules ->
rules.anyRequest().authenticated());
return http.build();
}Cost and verification
Preflight adds an extra browser round trip unless cached. A narrow policy costs little server CPU, but every new origin, method or header expands the cross-origin surface and should correspond to a real client requirement.
Common Mistakes
- Do not require a bearer token on the OPTIONS preflight and then call the browser broken.
- Do not treat a successful preflight as authorization for the actual request.
- Do not apply a broad origin wildcard to a credentialed browser flow.
Read next
Spring MVC CORS: a browser-origin policy is not authentication, Spring Security filter chain: authentication, CSRF and request order, Spring Security request matchers: order rules and cover the fallback, Spring Security CSRF: protect cookie-authenticated writes, Spring MockMvc security test: run the application filter chain.
Related security contract
Spring Security CSP rollout: enforce tested asset rules at the response edge.
