CORS & CSRF
IntermediateCORS allows browsers to make cross-origin API calls. CSRF protects stateful (cookie-based) apps from forged requests. Stateless REST APIs with JWT disable CSRF and configure CORS explicitly.
Overview
CORS (Cross-Origin Resource Sharing) is a browser mechanism — browsers block cross-origin requests unless the server sends the right CORS headers. CSRF (Cross-Site Request Forgery) exploits cookies — a malicious site tricks a logged-in user's browser into sending a request with their session cookie. Stateless REST APIs using JWT tokens in Authorization headers are not vulnerable to CSRF (a cross-site request can't access localStorage to steal the JWT), so CSRF protection can be disabled. Always configure CORS explicitly instead of using @CrossOrigin on every controller.
Global CORS Configuration
Configure CORS once via CorsConfigurationSource and wire it into Spring Security. This applies to all endpoints and handles preflight OPTIONS requests automatically.
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
// Allowed origins — never use "*" in production
config.setAllowedOrigins(List.of(
"https://aicancode.org",
"https://www.aicancode.org",
"http://localhost:3000" // dev
));
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"));
config.setAllowedHeaders(List.of(
"Authorization", "Content-Type", "X-Requested-With"
));
config.setExposedHeaders(List.of("X-Total-Count")); // headers JS can read
config.setAllowCredentials(true); // required for cookies/auth headers
config.setMaxAge(3600L); // preflight cache for 1 hour
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return source;
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
.csrf(AbstractHttpConfigurer::disable) // safe for stateless JWT APIs
// ...
.build();
}
}When NOT to Disable CSRF
If your app uses cookie-based sessions (e.g. a traditional web app with server-side rendering or Spring Session), CSRF protection must stay on. The double-submit cookie pattern or Synchronizer Token Pattern protects against forged requests.
// ✅ Stateless REST API (JWT in Authorization header) — CSRF safe to disable
// A cross-site attacker cannot read your localStorage to steal the JWT
.csrf(AbstractHttpConfigurer::disable)
// ⚠️ Stateful app (session cookie) — CSRF MUST stay enabled
.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
// ^ stores CSRF token in a readable cookie so JS can send it back
)
// Fine-grained: disable CSRF only for API endpoints, keep for MVC pages
.csrf(csrf -> csrf
.ignoringRequestMatchers("/api/**") // stateless — skip CSRF for /api
)
// What CSRF protection does:
// 1. Server generates a secret token, stores it in session + sends as header/cookie
// 2. Client must send the token back in X-CSRF-Token or _csrf param
// 3. Attacker's site cannot read the token (same-origin policy) — request rejected
// @CrossOrigin for controller-level overrides (avoid in favor of global config)
@CrossOrigin(origins = "https://aicancode.org")
@RestController
public class PublicController { ... }Key Points to Remember
- 1CORS is enforced by browsers, not the server — configure it to tell browsers which origins are allowed.
- 2Never use allowedOrigins("*") with allowCredentials(true) — browsers reject this combination.
- 3CSRF is only a risk for cookie-based authentication — stateless JWT APIs can safely disable it.
- 4Configure CORS globally via CorsConfigurationSource — avoid @CrossOrigin scattered across controllers.
- 5Spring Security's .cors() must reference the CorsConfigurationSource bean, or CORS headers won't be added to responses.
- 6OPTIONS preflight requests must be permitted without authentication — Spring Security handles this automatically when CORS is configured.
Interview Questions
Sign in to ask AriaWhat is the difference between CORS and CSRF?
Why can you disable CSRF for a stateless JWT API but not for a session-based app?
What does allowCredentials(true) require in terms of allowed origins?
What is a CORS preflight request and when does the browser send one?
How would you configure CORS to allow different origins per environment?
Ask Aria about CORS & CSRF
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.