Home Java CSRF Protection in Spring Security: Complete Guide with Examples
Intermediate 7 min · July 14, 2026

CSRF Protection in Spring Security: Complete Guide with Examples

Learn CSRF protection in Spring Security with production-tested examples, debugging tips, and real-world incident analysis.

N
Naren Founder & Principal Engineer

20+ years shipping production Java in banking & fintech. Everything here is grounded in real deployments.

Follow
Production
production tested
July 15, 2026
last updated
2,398
articles · all by Naren
Before you start⏱ 15-20 min read
  • Java 17+
  • Spring Boot 3.x
  • Spring Security 6.x
  • Basic understanding of HTTP and cookies
 ● Production Incident 🔎 Debug Guide ⚙ Triage Commands
Quick Answer
  • CSRF protection prevents malicious requests from authenticated users.
  • Spring Security enables CSRF by default for state-changing operations (POST, PUT, DELETE).
  • For REST APIs using stateless authentication (JWT), disable CSRF.
  • For traditional web apps, use token-based protection with SameSite cookies.
  • Always test CSRF with browser dev tools and security scanners.
✦ Definition~90s read
What is A Complete Guide to CSRF Protection in Spring Security?

CSRF protection is a security mechanism in Spring Security that prevents cross-site request forgery attacks by requiring a unique token for each state-changing request.

Imagine you're logged into your bank.
Plain-English First

Imagine you're logged into your bank. A bad guy sends you a link that, when clicked, transfers money out of your account without you knowing. CSRF protection is like a secret handshake that your browser and the bank agree on, so only real requests from you go through.

If you're building a web application with Spring Security, CSRF (Cross-Site Request Forgery) protection is one of those things that sounds simple but bites you in production. I've seen teams disable it because 'we have JWT, we're stateless' — and then wonder why their session-based admin panel gets hijacked. The truth is, CSRF protection isn't one-size-fits-all. In this guide, I'll show you exactly when to enable it, when to disable it, and how to configure it properly. We'll cover the default behavior in Spring Security 6.x, how to customize token generation, and what the official docs gloss over. By the end, you'll know how to protect your users without breaking your API.

What is CSRF and Why Should You Care?

Cross-Site Request Forgery (CSRF) is an attack that tricks an authenticated user into executing unwanted actions on a web application. Imagine a user logged into their bank. An attacker sends them a link to a malicious page that contains a hidden form submitting a transfer request. Since the user's browser automatically includes cookies (like session cookies), the bank server sees a legitimate request from an authenticated user. Spring Security's CSRF protection prevents this by requiring a unique token that the attacker cannot guess.

In Spring Security 6.x, CSRF protection is enabled by default for any request that modifies state (POST, PUT, DELETE, PATCH). For GET requests, it's typically not needed because they shouldn't change state. However, if you're building a pure REST API with stateless authentication (like JWT in Authorization header), you should disable CSRF because there's no session cookie to hijack. But — and this is a big but — if you store your JWT in a cookie, you still need CSRF protection.

Here's the default configuration in a Spring Boot 3.x app with Spring Security:

``java @Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .csrf(Customizer.withDefaults()) // enabled by default .authorizeHttpRequests(authz -> authz .anyRequest().authenticated() ) .formLogin(Customizer.withDefaults()); return http.build(); } } ``

This gives you a per-session CSRF token stored in the HttpSession. The token is exposed to the client via a cookie (by default named XSRF-TOKEN) or as a request attribute. The client must send this token back in a header (X-CSRF-TOKEN) or parameter (_csrf).

SecurityConfig.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf(Customizer.withDefaults())
            .authorizeHttpRequests(authz -> authz
                .anyRequest().authenticated()
            )
            .formLogin(Customizer.withDefaults());
        return http.build();
    }
}
⚠ Default CSRF Protection is Session-Based
📊 Production Insight
I once saw a team spend two days debugging 403 errors on their Angular app. They had disabled CSRF globally but forgot that their login endpoint was still protected. The fix was to disable CSRF only for stateless endpoints.
🎯 Key Takeaway
CSRF protection is enabled by default for state-changing requests in Spring Security. Understand your authentication mechanism before disabling it.

Configuring CSRF Protection: The Right Way

Spring Security provides several ways to configure CSRF protection. The most common is the default HttpSessionCsrfTokenRepository, but you can customize the token repository, the request matcher, and how the token is transmitted.

For traditional server-rendered applications (Thymeleaf, JSP), the default works fine. For Single Page Applications (SPAs) like Angular or React, you need to expose the token to JavaScript. That's where CookieCsrfTokenRepository comes in. It stores the token in a cookie (XSRF-TOKEN) that the client can read and send back.

``java http .csrf(csrf -> csrf .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) ); ``

Note: withHttpOnlyFalse() is required so that JavaScript can read the cookie. This is a security trade-off — if your site has an XSS vulnerability, an attacker could steal the token. But for SPAs, it's necessary.

You can also restrict CSRF protection to specific patterns using CsrfConfigurer.requireCsrfProtectionMatcher. For example, you might want to exclude WebSocket endpoints:

``java http .csrf(csrf -> csrf .requireCsrfProtectionMatcher(new RequestMatcher() { private Pattern pattern = Pattern.compile("/websocket/.*"); @Override public boolean matches(HttpServletRequest request) { return !pattern.matcher(request.getRequestURI()).matches(); } }) ); ``

But the most important configuration is knowing when to disable CSRF entirely. For REST APIs that use Bearer token authentication (JWT in Authorization header), disable CSRF:

``java http .csrf(csrf -> csrf.disable()) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); ``

I've seen teams blindly copy this without understanding why. If you disable CSRF but still use session cookies, you're vulnerable.

CsrfConfigExample.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -> csrf
                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
                .requireCsrfProtectionMatcher(new RequestMatcher() {
                    private Pattern pattern = Pattern.compile("/api/public/.*");
                    @Override
                    public boolean matches(HttpServletRequest request) {
                        return !pattern.matcher(request.getRequestURI()).matches();
                    }
                })
            )
            .authorizeHttpRequests(authz -> authz
                .requestMatchers("/api/public/**").permitAll()
                .anyRequest().authenticated()
            );
        return http.build();
    }
}
🔥CookieCsrfTokenRepository and HttpOnly
📊 Production Insight
A common mistake is to use CookieCsrfTokenRepository with HttpOnly=true, then wonder why Angular can't send the token. The cookie is not readable by JS. Always set HttpOnly to false for SPAs, or use a custom endpoint.
🎯 Key Takeaway
Use CookieCsrfTokenRepository for SPAs, but be aware of the XSS risk. Disable CSRF only for stateless APIs with Bearer token authentication.

CSRF with Angular, React, and Other SPAs

Integrating CSRF protection with a frontend framework requires coordination. The backend must expose the token, and the frontend must include it in every state-changing request.

For Angular, the HttpClient module has built-in CSRF support. It looks for a cookie named XSRF-TOKEN and automatically sends it as a header (X-XSRF-TOKEN). This works seamlessly with CookieCsrfTokenRepository. However, there's a catch: Angular only sends the token for mutating requests (POST, PUT, DELETE, PATCH) and only to the same origin. That's perfect.

``typescript import { HttpClientModule } from '@angular/common/http'; // Angular automatically handles CSRF token ``

For React, you need to manually read the cookie and set the header. Use a library like js-cookie:

```javascript import Cookies from 'js-cookie';

const csrfToken = Cookies.get('XSRF-TOKEN'); fetch('/api/transfer', { method: 'POST', headers: { 'X-XSRF-TOKEN': csrfToken, 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ amount: 100 }) }); ```

A common pitfall: forgetting to set credentials: 'include' for cross-origin requests. Without it, the browser won't send cookies, and the CSRF token won't be available.

What the docs don't tell you: If you use a custom header name (e.g., X-CSRF-TOKEN instead of X-XSRF-TOKEN), you must configure Angular to expect it:

```typescript import { HttpClientXsrfModule } from '@angular/common/http';

@NgModule({ imports: [ HttpClientModule, HttpClientXsrfModule.withOptions({ cookieName: 'CSRF-TOKEN', headerName: 'X-CSRF-TOKEN' }) ] }) ```

CsrfAngularExample.javaJAVA
1
2
3
4
5
6
7
// Backend: Spring Security configuration
http
    .csrf(csrf -> csrf
        .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
    );

// Angular automatically uses XSRF-TOKEN cookie and X-XSRF-TOKEN header
💡SameSite Cookie Attribute
📊 Production Insight
I debugged an issue where the CSRF token was being sent but the server still returned 403. Turns out, the token was being set on a different path than the request. Always ensure the cookie path matches your API endpoints.
🎯 Key Takeaway
Angular has built-in CSRF support. React requires manual header setup. Always include credentials for cross-origin requests.

What the Official Docs Won't Tell You

Here are the gotchas I've encountered in production that the Spring Security reference documentation either glosses over or omits entirely.

1. CSRF Token and Session Fixation The default CSRF token is stored in the session. If you change the session ID (e.g., after login), the old token becomes invalid. This is actually a security feature, but it means you must regenerate the token on login. Spring Security does this automatically, but if you're using a custom authentication filter, you might forget to clear the token repository.

2. Multiple Tabs Issue If a user opens multiple tabs, each tab gets the same session and thus the same CSRF token. That's fine — the token is per-session, not per-request. But if one tab logs out, the session is invalidated, and the other tabs will get 403 errors. This is expected behavior, but users will blame your app.

3. CSRF and File Uploads File uploads often use multipart/form-data. The CSRF token should be included as a form field, not in the URL. If you forget, the upload will fail with 403. Always include a hidden input for the token in your upload form.

4. Custom Header Names By default, Spring Security expects the token in a header named X-CSRF-TOKEN or a parameter named _csrf. If you change the header name on the client, you must also configure the server. Use CsrfTokenRequestAttributeHandler to customize.

5. Stateless APIs with Cookie-Based JWTs This is the biggest trap. You disable CSRF because you're stateless, but you store the JWT in a cookie. Now you're vulnerable again. The fix: either store the JWT in a header (Authorization: Bearer ...) or keep CSRF enabled.

6. Testing CSRF with MockMvc When writing tests, you need to include the CSRF token. Spring Security Test provides .with(csrf()) for MockMvc. If you forget, your POST tests will fail with 403.

7. CSRF and WebSockets WebSocket connections are not protected by CSRF by default because they use a different handshake. You need to implement custom protection, such as validating an origin header or using a token in the handshake request.

CsrfTestExample.javaJAVA
1
2
3
4
5
6
7
@Test
public void testPostWithCsrf() throws Exception {
    mockMvc.perform(post("/api/transfer")
            .with(csrf())  // from spring-security-test
            .param("amount", "100"))
        .andExpect(status().isOk());
}
⚠ JWT in Cookie + CSRF Disabled = Vulnerable
📊 Production Insight
I once spent hours debugging why a React app worked on localhost but failed in production. The issue was that the CSRF cookie had SameSite=Strict, which prevented cross-origin requests from the CDN. We changed it to SameSite=Lax.
🎯 Key Takeaway
The official docs miss many real-world pitfalls: session fixation, multiple tabs, file uploads, custom headers, and JWT-in-cookie scenarios.

Advanced CSRF Configuration: Custom Token Repository and Stateless Tokens

For high-security applications, you might want to generate CSRF tokens without relying on sessions. This is useful for stateless architectures or when you want to avoid server-side state. You can implement a custom CsrfTokenRepository that generates tokens based on a secret key and user identity.

Here's a simple example of a stateless token repository using HMAC:

```java public class StatelessCsrfTokenRepository implements CsrfTokenRepository { private final String secret;

public StatelessCsrfTokenRepository(String secret) { this.secret = secret; }

@Override public CsrfToken generateToken(HttpServletRequest request) { String token = UUID.randomUUID().toString(); return new DefaultCsrfToken("X-CSRF-TOKEN", "_csrf", token); }

@Override public void saveToken(CsrfToken token, HttpServletRequest request, HttpServletResponse response) { if (token == null) { // Remove token Cookie cookie = new Cookie("XSRF-TOKEN", ""); cookie.setMaxAge(0); response.addCookie(cookie); } else { // Store in cookie Cookie cookie = new Cookie("XSRF-TOKEN", token.getToken()); cookie.setHttpOnly(false); cookie.setSecure(true); cookie.setPath("/"); response.addCookie(cookie); } }

@Override public CsrfToken loadToken(HttpServletRequest request) { // Read from cookie Cookie[] cookies = request.getCookies(); if (cookies != null) { for (Cookie cookie : cookies) { if ("XSRF-TOKEN".equals(cookie.getName())) { return new DefaultCsrfToken("X-CSRF-TOKEN", "_csrf", cookie.getValue()); } } } return null; } } ```

This is a simplified version. In production, you'd want to sign the token with a secret to prevent tampering. You can also use JWT as the CSRF token itself, embedding user identity and expiration.

Another advanced feature is to use a custom CsrfTokenRequestAttributeHandler to change how the token is resolved from the request. By default, Spring Security looks for the token in the header or parameter. You can override this to, say, validate a signature.

StatelessCsrfTokenRepository.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class StatelessCsrfTokenRepository implements CsrfTokenRepository {
    private final String secret;

    public StatelessCsrfTokenRepository(String secret) {
        this.secret = secret;
    }

    @Override
    public CsrfToken generateToken(HttpServletRequest request) {
        String token = UUID.randomUUID().toString();
        return new DefaultCsrfToken("X-CSRF-TOKEN", "_csrf", token);
    }

    @Override
    public void saveToken(CsrfToken token, HttpServletRequest request, HttpServletResponse response) {
        if (token == null) {
            Cookie cookie = new Cookie("XSRF-TOKEN", "");
            cookie.setMaxAge(0);
            response.addCookie(cookie);
        } else {
            Cookie cookie = new Cookie("XSRF-TOKEN", token.getToken());
            cookie.setHttpOnly(false);
            cookie.setSecure(true);
            cookie.setPath("/");
            response.addCookie(cookie);
        }
    }

    @Override
    public CsrfToken loadToken(HttpServletRequest request) {
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if ("XSRF-TOKEN".equals(cookie.getName())) {
                    return new DefaultCsrfToken("X-CSRF-TOKEN", "_csrf", cookie.getValue());
                }
            }
        }
        return null;
    }
}
🔥Stateless CSRF Tokens
📊 Production Insight
I built a stateless CSRF token system for a microservices architecture where sessions were not shared. We used a shared secret and HMAC to validate tokens across services. It worked well but added latency.
🎯 Key Takeaway
Advanced CSRF configurations include custom token repositories for stateless architectures. Use HMAC or JWT to sign tokens for security.

Testing CSRF Protection

Testing CSRF protection is critical, yet often overlooked. You need to ensure that requests without a valid token are rejected, and that legitimate requests succeed.

Spring Security Test provides a convenient .with(csrf()) method for MockMvc tests. Here's an example:

```java @SpringBootTest @AutoConfigureMockMvc public class CsrfTest { @Autowired private MockMvc mockMvc;

@Test public void testPostWithoutCsrf() throws Exception { mockMvc.perform(post("/api/transfer") .param("amount", "100")) .andExpect(status().isForbidden()); }

@Test public void testPostWithCsrf() throws Exception { mockMvc.perform(post("/api/transfer") .with(csrf()) .param("amount", "100")) .andExpect(status().isOk()); } } ```

For integration tests with a real browser, use tools like Selenium or Playwright. You can also use security scanners like OWASP ZAP to automate CSRF testing.

A common mistake: forgetting to include the CSRF token in WebClient or RestTemplate calls. If you're making server-side requests to another service that requires CSRF, you must pass the token. But in a microservices architecture, you typically disable CSRF for internal APIs.

What the docs don't tell you: MockMvc's .with(csrf()) only works if CSRF protection is enabled. If you've disabled it globally, the test will pass even without the token. Always write both positive and negative tests.

CsrfTest.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@SpringBootTest
@AutoConfigureMockMvc
public class CsrfTest {
    @Autowired
    private MockMvc mockMvc;

    @Test
    public void testPostWithoutCsrf() throws Exception {
        mockMvc.perform(post("/api/transfer")
                .param("amount", "100"))
            .andExpect(status().isForbidden());
    }

    @Test
    public void testPostWithCsrf() throws Exception {
        mockMvc.perform(post("/api/transfer")
                .with(csrf())
                .param("amount", "100"))
            .andExpect(status().isOk());
    }
}
💡Test Both With and Without CSRF
📊 Production Insight
I once saw a test suite pass because the developer had disabled CSRF in the test profile. The tests were useless. Always verify that your test configuration matches production.
🎯 Key Takeaway
Use MockMvc with .with(csrf()) for unit tests. Always test both valid and invalid CSRF scenarios.

CSRF and CORS: The Dynamic Duo

CSRF and CORS are often confused but are different security mechanisms. CORS controls which origins can access your resources, while CSRF prevents unauthorized requests from authenticated users. They work together: CORS allows or blocks cross-origin requests, and CSRF ensures that allowed requests are intentional.

If you have a frontend on a different origin (e.g., React on localhost:3000, backend on localhost:8080), you need both CORS and CSRF configuration.

``java @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); configuration.setAllowedOrigins(Arrays.asList("http://localhost:3000")); configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE")); configuration.setAllowCredentials(true); configuration.setAllowedHeaders(Arrays.asList("")); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/*", configuration); return source; } ``

``java http .cors(Customizer.withDefaults()) .csrf(csrf -> csrf .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) ); ``

A critical gotcha: CORS preflight requests (OPTIONS) must not require CSRF tokens. Spring Security handles this automatically by not applying CSRF to OPTIONS requests, but if you have a custom filter, ensure it doesn't block preflight.

Another gotcha: If you set allowedOrigins to "*" with allowCredentials=true, the browser will reject the request. CORS spec doesn't allow wildcard origins with credentials. Always specify explicit origins.

CorsCsrfConfig.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .cors(Customizer.withDefaults())
            .csrf(csrf -> csrf
                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
            )
            .authorizeHttpRequests(authz -> authz
                .anyRequest().authenticated()
            );
        return http.build();
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("http://localhost:3000"));
        configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE"));
        configuration.setAllowCredentials(true);
        configuration.setAllowedHeaders(Arrays.asList("*"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}
⚠ CORS and CSRF Work Together
📊 Production Insight
I've seen teams disable CSRF because they thought CORS was enough. Then an attacker used a DNS rebinding attack to bypass CORS and perform CSRF. Always use both.
🎯 Key Takeaway
CORS and CSRF are complementary. Configure both properly for cross-origin requests.
● Production incidentPOST-MORTEMseverity: high

The Fintech Payment Double-Charge Incident

Symptom
Users reported being charged twice for the same transaction. Logs showed duplicate POST requests to /api/payments with the same payload.
Assumption
Developers assumed that because the API used JWT tokens, CSRF protection was unnecessary.
Root cause
A malicious script on a third-party site exploited the lack of CSRF token validation. The script submitted the payment form multiple times, and since the JWT was valid (stored in cookie), the server processed each request.
Fix
Re-enabled CSRF protection for all state-changing endpoints. Added idempotency keys to prevent duplicate payments. Implemented SameSite=Strict cookie attribute.
Key lesson
  • Never disable CSRF globally without understanding the risk.
  • JWT in cookies is still vulnerable to CSRF without proper token validation.
  • Use idempotency keys for critical operations like payments.
  • Always test with a security scanner like OWASP ZAP.
  • SameSite cookie attribute is not a silver bullet; combine with CSRF tokens.
Production debug guideSymptom to Action4 entries
Symptom · 01
HTTP 403 Forbidden with 'Invalid CSRF Token' on POST requests
Fix
Check if the request includes the CSRF token header or parameter. For Angular apps, ensure you're using withCredentials and the token is being fetched from the server.
Symptom · 02
CSRF token not found in response when using REST API
Fix
Verify CSRF protection is enabled. If using stateless authentication, disable CSRF. If using sessions, ensure the endpoint returns the token via a cookie or header.
Symptom · 03
Token mismatch error after login
Fix
The token is bound to the session. After login, the session ID changes, invalidating the old token. Regenerate the token on login by clearing the old one.
Symptom · 04
Cross-origin requests failing with CSRF errors
Fix
Check CORS configuration. CSRF tokens must be sent with the request. For cross-origin, ensure the server sets Access-Control-Allow-Credentials: true and the client includes credentials.
★ Quick Debug Cheat SheetImmediate actions for common CSRF issues
403 Forbidden on POST
Immediate action
Check network tab for CSRF token header (X-CSRF-TOKEN) or parameter (_csrf).
Commands
curl -v -X POST -H 'X-CSRF-TOKEN: <token>' http://localhost:8080/api/endpoint
grep -i csrf /var/log/spring/app.log
Fix now
Ensure the client sends the token. For Thymeleaf, include <input type='hidden' th:name='${_csrf.parameterName}' th:value='${_csrf.token}' /> in forms.
Token not generated+
Immediate action
Check if CSRF protection is enabled and session is active.
Commands
curl -v http://localhost:8080/login 2>&1 | grep -i csrf
Check /actuator/health for app status
Fix now
Enable CSRF in SecurityFilterChain: .csrf(csrf -> csrf.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()))
Token mismatch after login+
Immediate action
Regenerate token after login by clearing the old token repository.
Commands
Check if session ID changes after login
Verify CsrfTokenRepository implementation
Fix now
Use CustomCsrfTokenRepository that clears token on authentication success.
FeatureDefault CSRFCookieCsrfTokenRepositoryDisabled
Token storageHttpSessionCookie (XSRF-TOKEN)None
Client requiredForm parameter or headerCookie + headerNothing
Best forServer-rendered appsSPAs (Angular, React)Stateless REST APIs
XSS riskLowHigher (cookie readable by JS)None
⚙ Quick Reference
7 commands from this guide
FileCommand / CodePurpose
SecurityConfig.java@ConfigurationWhat is CSRF and Why Should You Care?
CsrfConfigExample.java@ConfigurationConfiguring CSRF Protection
CsrfAngularExample.javahttpCSRF with Angular, React, and Other SPAs
CsrfTestExample.java@TestWhat the Official Docs Won't Tell You
StatelessCsrfTokenRepository.javapublic class StatelessCsrfTokenRepository implements CsrfTokenRepository {Advanced CSRF Configuration
CsrfTest.java@SpringBootTestTesting CSRF Protection
CorsCsrfConfig.java@ConfigurationCSRF and CORS

Key takeaways

1
CSRF protection prevents malicious requests from authenticated users. Enable it for session-based web apps.
2
Disable CSRF only for stateless REST APIs that use Bearer token authentication.
3
Use CookieCsrfTokenRepository with HttpOnly=false for SPAs. Test both positive and negative scenarios.
4
CORS and CSRF are complementary; don't disable one because you have the other.
5
Always regenerate CSRF token after login to prevent session fixation.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01SENIOR
How does Spring Security's CSRF protection work under the hood?
Q02JUNIOR
When would you disable CSRF protection in a Spring Boot application?
Q03SENIOR
How do you implement CSRF protection for a Spring Boot application with ...
Q01 of 03SENIOR

How does Spring Security's CSRF protection work under the hood?

ANSWER
Spring Security generates a unique token per session, stores it (e.g., in HttpSession or cookie), and requires the client to include it in state-changing requests. The CsrfFilter compares the submitted token with the stored one.
FAQ · 3 QUESTIONS

Frequently Asked Questions

01
Should I disable CSRF for REST APIs?
02
How do I get the CSRF token in Thymeleaf?
03
What is the difference between XSRF-TOKEN and X-CSRF-TOKEN?
N
Naren Founder & Principal Engineer

20+ years shipping production Java in banking & fintech. Everything here is grounded in real deployments.

Follow
Verified
production tested
July 15, 2026
last updated
2,398
articles · all by Naren
🔥

That's Spring Security. Mark it forged?

7 min read · try the examples if you haven't

Previous
Disabling Spring Security for a Specific Profile in Spring Boot
30 / 31 · Spring Security
Next
Preventing Cross-Site Scripting (XSS) Attacks in Spring Applications