Home/Learn/Spring Boot/CORS & CSRF

CORS & CSRF

Intermediate
Security

CORS 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.

Java — global CorsConfigurationSource wired into Security
@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.

Java — when to disable/keep CSRF, controller-level override
// ✅ 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 Aria
1

What is the difference between CORS and CSRF?

EasyAmazon
2

Why can you disable CSRF for a stateless JWT API but not for a session-based app?

MediumThoughtWorks
3

What does allowCredentials(true) require in terms of allowed origins?

MediumRazorpay
4

What is a CORS preflight request and when does the browser send one?

MediumAtlassian
5

How would you configure CORS to allow different origins per environment?

MediumWipro

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.

Loading discussion…