CORS done right

Spring Security · lesson 11 of 31 · 3 min read

Configure cross-origin access in the security chain so preflight requests stop failing.

Open this lesson in the learning hub

Key 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 your CorsConfigurationSource bean.
  • @CrossOrigin on a controller is too late if the chain rejects the preflight first. Prefer one central config.
  • allowCredentials(true) forbids *. List exact origins, or use allowedOriginPatterns for 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.