Security Headers for Spring Boot REST APIs – OWASP Guide
Fast recall
Quick revision
Core answer
X-Content-Type-Options: nosniff, X-Frame-Options: DENY, cache-control headers, and X-XSS-Protection: 0; it adds HSTS only on HTTPS responses. CSP and frame controls mainly protect browser-rendered documents and must match the frontend. A JSON API should send the correct content type and nosniff, protect sensitive caching, and configure CORS only for the origins and credential model it intentionally supports. CORS is a browser read policy, not authentication.Interview-ready guide
Interview answer
Security headers should match what an endpoint serves. For a JSON API, I would send the correct Content-Type, keep X-Content-Type-Options: nosniff, and use cache controls that prevent sensitive responses being stored where they should not be. HSTS can tell browsers to use HTTPS on later visits, but it is sent only over HTTPS and needs careful rollout for subdomains and preload.
For pages that render HTML, framing controls and a tested Content Security Policy can reduce clickjacking and XSS impact. CSP is defense in depth; it does not replace output encoding or safe DOM use. Spring Security adds several defaults, including nosniff, X-Frame-Options: DENY, cache-control headers, and the modern X-XSS-Protection: 0 value.
CORS is separate from these hardening headers. It controls what browser origins may read or make certain cross-origin requests. It is not authentication and does not stop direct HTTP clients. A credentialed private frontend normally gets a finite origin allowlist, while a deliberately public credentialless API may allow *. I would verify the final headers after the reverse proxy or CDN because infrastructure can add, remove, or duplicate them.
Security Headers for Spring Boot REST APIs – OWASP Guide
Direct Answer
Choose response headers from what the endpoint serves. Spring Security commonly adds X-Content-Type-Options: nosniff, X-Frame-Options: DENY, cache-control headers, and X-XSS-Protection: 0; it adds HSTS only on HTTPS responses. CSP and frame controls mainly protect browser-rendered documents and must match the frontend. A JSON API should send the correct content type and nosniff, protect sensitive caching, and configure CORS only for the origins and credential model it intentionally supports. CORS is a browser read policy, not authentication.
Code Example
@Bean
SecurityFilterChain security(HttpSecurity http) throws Exception {
http.headers(headers -> headers
.contentTypeOptions(Customizer.withDefaults())
.frameOptions(frame -> frame.deny())
.httpStrictTransportSecurity(hsts -> hsts
.maxAgeInSeconds(31_536_000)
.includeSubDomains(false)));
http.cors(cors -> cors.configurationSource(corsConfigurationSource()));
return http.build();
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration c = new CorsConfiguration();
c.setAllowedOrigins(List.of("https://app.example"));
c.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
c.setAllowedHeaders(List.of("Authorization", "Content-Type", "X-CSRF-TOKEN"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/api/**", c);
return source;
}
Add CSP only to responses that render documents, and build it from the page's real script/style needs. Enable HSTS subdomains or preload only after every affected host supports HTTPS.
Common Mistakes & Pitfalls
- Disabling all headers to fix CORS errors: Freshers often disable security headers entirely when they encounter CORS issues, thinking headers are the problem. The real fix is configuring the CORS bean properly—never remove protective headers.
- Using wildcard CORS in production: Setting allowedOrigins to asterisk allows any website to call your API. Always whitelist specific frontend domains like https://yourdomain.com in production environments.
- Not understanding default behavior: Many freshers manually configure headers that Spring Security already adds, creating duplicate or conflicting settings. Check what's automatic first using browser DevTools Network tab to see response headers.
Check your understanding
Answer these without looking back at the article.
- What is a clickjacking attack and how does X-Frame-Options prevent it?
- How do you verify which security headers your Spring Boot app is returning?
- Why doesn't Spring Security set a Content Security Policy by default?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.