You can use Security HTTP Response Headers to increase the security of web applications. This section is dedicated to servlet-based support for Security HTTP Response Headers.
Spring Security provides a default set of Security HTTP Response Headers to provide secure defaults. While each of these headers are considered best practice, it should be noted that not all clients use the headers, so additional testing is encouraged.
You can customize specific headers.
For example, assume that you want the defaults but you wish to specify SAMEORIGIN
for X-Frame-Options.
You can do so with the following configuration:
- Java
-
@Configuration @EnableWebSecurity public class WebSecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http // ... .headers(headers -> headers .frameOptions(frameOptions -> frameOptions .sameOrigin() ) ); return http.build(); } }
- XML
-
<http> <!-- ... --> <headers> <frame-options policy="SAMEORIGIN" /> </headers> </http>
- Kotlin
-
@Configuration @EnableWebSecurity class SecurityConfig { @Bean open fun filterChain(http: HttpSecurity): SecurityFilterChain { http { // ... headers { frameOptions { sameOrigin = true } } } return http.build() } }
If you do not want the defaults to be added and want explicit control over what should be used, you can disable the defaults. The next code listing shows how to do so.
If you use Spring Security’s configuration, the following adds only Cache Control:
- Java
-
@Configuration @EnableWebSecurity public class WebSecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http // ... .headers(headers -> headers // do not use any default headers unless explicitly listed .defaultsDisabled() .cacheControl(withDefaults()) ); return http.build(); } }
- XML
-
<http> <!-- ... --> <headers defaults-disabled="true"> <cache-control/> </headers> </http>
- Kotlin
-
@Configuration @EnableWebSecurity class SecurityConfig { @Bean open fun filterChain(http: HttpSecurity): SecurityFilterChain { http { // ... headers { // do not use any default headers unless explicitly listed defaultsDisabled = true cacheControl { } } } return http.build() } }
If necessary, you can disable all of the HTTP Security response headers with the following configuration:
- Java
-
@Configuration @EnableWebSecurity public class WebSecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http // ... .headers(headers -> headers.disable()); return http.build(); } }
- XML
-
<http> <!-- ... --> <headers disabled="true" /> </http>
- Kotlin
-
@Configuration @EnableWebSecurity class SecurityConfig { @Bean open fun filterChain(http: HttpSecurity): SecurityFilterChain { http { // ... headers { disable() } } return http.build() } }
Spring Security includes Cache Control headers by default.
However, if you actually want to cache specific responses, your application can selectively invoke HttpServletResponse.setHeader(String,String)
to override the header set by Spring Security.
You can use this to ensure that content (such as CSS, JavaScript, and images) is properly cached.
When you use Spring Web MVC, this is typically done within your configuration. You can find details on how to do this in the Static Resources portion of the Spring Reference documentation
If necessary, you can also disable Spring Security’s cache control HTTP response headers.
- Java
-
@Configuration @EnableWebSecurity public class WebSecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http // ... .headers(headers -> headers .cacheControl(cache -> cache.disable()) ); return http.build(); } }
- XML
-
<http> <!-- ... --> <headers> <cache-control disabled="true"/> </headers> </http>
- Kotlin
-
@Configuration @EnableWebSecurity class SecurityConfig { @Bean open fun filterChain(http: HttpSecurity): SecurityFilterChain { http { headers { cacheControl { disable() } } } return http.build() } }
Spring Security includes Content-Type headers by default. However, you can disable it:
- Java
-
@Configuration @EnableWebSecurity public class WebSecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http // ... .headers(headers -> headers .contentTypeOptions(contentTypeOptions -> contentTypeOptions.disable()) ); return http.build(); } }
- XML
-
<http> <!-- ... --> <headers> <content-type-options disabled="true"/> </headers> </http>
- Kotlin
-
@Configuration @EnableWebSecurity class SecurityConfig { @Bean open fun filterChain(http: HttpSecurity): SecurityFilterChain { http { headers { contentTypeOptions { disable() } } } return http.build() } }
By default, Spring Security provides the Strict Transport Security header. However, you can explicitly customize the results. The following example explicitly provides HSTS:
- Java
-
@Configuration @EnableWebSecurity public class WebSecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http // ... .headers(headers -> headers .httpStrictTransportSecurity(hsts -> hsts .includeSubDomains(true) .preload(true) .maxAgeInSeconds(31536000) ) ); return http.build(); } }
- XML
-
<http> <!-- ... --> <headers> <hsts include-subdomains="true" max-age-seconds="31536000" preload="true" /> </headers> </http>
- Kotlin
-
@Configuration @EnableWebSecurity class SecurityConfig { @Bean open fun filterChain(http: HttpSecurity): SecurityFilterChain { http { headers { httpStrictTransportSecurity { includeSubDomains = true preload = true maxAgeInSeconds = 31536000 } } } return http.build() } }
Spring Security provides servlet support for HTTP Public Key Pinning, but it is no longer recommended.
You can enable HPKP headers with the following configuration:
- Java
-
@Configuration @EnableWebSecurity public class WebSecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http // ... .headers(headers -> headers .httpPublicKeyPinning(hpkp -> hpkp .includeSubDomains(true) .reportUri("https://example.net/pkp-report") .addSha256Pins("d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=", "E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=") ) ); return http.build(); } }
- XML
-
<http> <!-- ... --> <headers> <hpkp include-subdomains="true" report-uri="https://example.net/pkp-report"> <pins> <pin algorithm="sha256">d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=</pin> <pin algorithm="sha256">E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=</pin> </pins> </hpkp> </headers> </http>
- Kotlin
-
@Configuration @EnableWebSecurity class SecurityConfig { @Bean open fun filterChain(http: HttpSecurity): SecurityFilterChain { http { headers { httpPublicKeyPinning { includeSubDomains = true reportUri = "https://example.net/pkp-report" pins = mapOf("d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=" to "sha256", "E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=" to "sha256") } } } return http.build() } }
By default, Spring Security instructs browsers to block reflected XSS attacks by using the X-Frame-Options.
For example, the following configuration specifies that Spring Security should no longer instruct browsers to block the content:
- Java
-
@Configuration @EnableWebSecurity public class WebSecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http // ... .headers(headers -> headers .frameOptions(frameOptions -> frameOptions .sameOrigin() ) ); return http.build(); } }
- XML
-
<http> <!-- ... --> <headers> <frame-options policy="SAMEORIGIN" /> </headers> </http>
- Kotlin
-
@Configuration @EnableWebSecurity class SecurityConfig { @Bean open fun filterChain(http: HttpSecurity): SecurityFilterChain { http { headers { frameOptions { sameOrigin = true } } } return http.build() } }
By default, Spring Security instructs browsers to disable the XSS Auditor by using <<headers-xss-protection,X-XSS-Protection header>. However, you can change this default. For example, the following configuration specifies that Spring Security instruct compatible browsers to enable filtering, and block the content:
- Java
-
@Configuration @EnableWebSecurity public class WebSecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http // ... .headers(headers -> headers .xssProtection(xss -> xss .headerValue(XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK) ) ); return http.build(); } }
- XML
-
<http> <!-- ... --> <headers> <xss-protection headerValue="1; mode=block"/> </headers> </http>
- Kotlin
-
@Configuration @EnableWebSecurity class SecurityConfig { @Bean open fun filterChain(http: HttpSecurity): SecurityFilterChain { // ... http { headers { xssProtection { headerValue = XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK } } } return http.build() } }
Spring Security does not add Content Security Policy by default, because a reasonable default is impossible to know without knowing the context of the application. The web application author must declare the security policy (or policies) to enforce or monitor for the protected resources.
Consider the following security policy:
Content-Security-Policy: script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/
Given the preceding security policy, you can enable the CSP header:
- Java
-
@Configuration @EnableWebSecurity public class WebSecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http // ... .headers(headers -> headers .contentSecurityPolicy(csp -> csp .policyDirectives("script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/") ) ); return http.build(); } }
- XML
-
<http> <!-- ... --> <headers> <content-security-policy policy-directives="script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/" /> </headers> </http>
- Kotlin
-
@Configuration @EnableWebSecurity class SecurityConfig { @Bean open fun filterChain(http: HttpSecurity): SecurityFilterChain { http { // ... headers { contentSecurityPolicy { policyDirectives = "script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/" } } } return http.build() } }
To enable the CSP report-only
header, provide the following configuration:
- Java
-
@Configuration @EnableWebSecurity public class WebSecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http // ... .headers(headers -> headers .contentSecurityPolicy(csp -> csp .policyDirectives("script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/") .reportOnly() ) ); return http.build(); } }
- XML
-
<http> <!-- ... --> <headers> <content-security-policy policy-directives="script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/" report-only="true" /> </headers> </http>
- Kotlin
-
@Configuration @EnableWebSecurity class SecurityConfig { @Bean open fun filterChain(http: HttpSecurity): SecurityFilterChain { http { // ... headers { contentSecurityPolicy { policyDirectives = "script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/" reportOnly = true } } } return http.build() } }
Spring Security does not add Referrer Policy headers by default. You can enable the Referrer Policy header by using the configuration:
- Java
-
@Configuration @EnableWebSecurity public class WebSecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http // ... .headers(headers -> headers .referrerPolicy(referrer -> referrer .policy(ReferrerPolicy.SAME_ORIGIN) ) ); return http.build(); } }
- XML
-
<http> <!-- ... --> <headers> <referrer-policy policy="same-origin" /> </headers> </http>
- Kotlin
-
@Configuration @EnableWebSecurity class SecurityConfig { @Bean open fun filterChain(http: HttpSecurity): SecurityFilterChain { http { // ... headers { referrerPolicy { policy = ReferrerPolicy.SAME_ORIGIN } } } return http.build() } }
Spring Security does not add Feature Policy headers by default.
Consider the following Feature-Policy
header:
Feature-Policy: geolocation 'self'
You can enable the preceding feature policy header by using the following configuration:
- Java
-
@Configuration @EnableWebSecurity public class WebSecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http // ... .headers(headers -> headers .featurePolicy("geolocation 'self'") ); return http.build(); } }
- XML
-
<http> <!-- ... --> <headers> <feature-policy policy-directives="geolocation 'self'" /> </headers> </http>
- Kotlin
-
@Configuration @EnableWebSecurity class SecurityConfig { @Bean open fun filterChain(http: HttpSecurity): SecurityFilterChain { http { // ... headers { featurePolicy("geolocation 'self'") } } return http.build() } }
Spring Security does not add Permissions Policy headers by default.
Consider the following Permissions-Policy
header:
Permissions-Policy: geolocation=(self)
You can enable the preceding permissions policy header using the following configuration:
- Java
-
@Configuration @EnableWebSecurity public class WebSecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http // ... .headers(headers -> headers .permissionsPolicy(permissions -> permissions .policy("geolocation=(self)") ) ); return http.build(); } }
- XML
-
<http> <!-- ... --> <headers> <permissions-policy policy="geolocation=(self)" /> </headers> </http>
- Kotlin
-
@Configuration @EnableWebSecurity class SecurityConfig { @Bean open fun filterChain(http: HttpSecurity): SecurityFilterChain { http { // ... headers { permissionPolicy { policy = "geolocation=(self)" } } } return http.build() } }
Spring Security does not add Clear-Site-Data headers by default. Consider the following Clear-Site-Data header:
Clear-Site-Data: "cache", "cookies"
You can send the preceding header on log out with the following configuration:
- Java
-
@Configuration @EnableWebSecurity public class WebSecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http // ... .logout((logout) -> logout .addLogoutHandler(new HeaderWriterLogoutHandler(new ClearSiteDataHeaderWriter(CACHE, COOKIES))) ); return http.build(); } }
- Kotlin
-
@Configuration @EnableWebSecurity class SecurityConfig { @Bean open fun filterChain(http: HttpSecurity): SecurityFilterChain { http { // ... logout { addLogoutHandler(HeaderWriterLogoutHandler(ClearSiteDataHeaderWriter(CACHE, COOKIES))) } } return http.build() } }
Spring Security has mechanisms to make it convenient to add the more common security headers to your application. However, it also provides hooks to enable adding custom headers.
There may be times when you wish to inject custom security headers that are not supported out of the box into your application. Consider the following custom security header:
X-Custom-Security-Header: header-value
Given the preceding header, you could add the headers to the response by using the following configuration:
- Java
-
@Configuration @EnableWebSecurity public class WebSecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http // ... .headers(headers -> headers .addHeaderWriter(new StaticHeadersWriter("X-Custom-Security-Header","header-value")) ); return http.build(); } }
- XML
-
<http> <!-- ... --> <headers> <header name="X-Custom-Security-Header" value="header-value"/> </headers> </http>
- Kotlin
-
@Configuration @EnableWebSecurity class SecurityConfig { @Bean open fun filterChain(http: HttpSecurity): SecurityFilterChain { http { // ... headers { addHeaderWriter(StaticHeadersWriter("X-Custom-Security-Header","header-value")) } } return http.build() } }
When the namespace or Java configuration does not support the headers you want, you can create a custom HeadersWriter
instance or even provide a custom implementation of the HeadersWriter
.
The next example use a custom instance of XFrameOptionsHeaderWriter
.
If you wanted to explicitly configure X-Frame-Options, you could do so with the following configuration:
- Java
-
@Configuration @EnableWebSecurity public class WebSecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http // ... .headers(headers -> headers .addHeaderWriter(new XFrameOptionsHeaderWriter(XFrameOptionsMode.SAMEORIGIN)) ); return http.build(); } }
- XML
-
<http> <!-- ... --> <headers> <header ref="frameOptionsWriter"/> </headers> </http> <!-- Requires the c-namespace. See https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-c-namespace --> <beans:bean id="frameOptionsWriter" class="org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter" c:frameOptionsMode="SAMEORIGIN"/>
- Kotlin
-
@Configuration @EnableWebSecurity class SecurityConfig { @Bean open fun filterChain(http: HttpSecurity): SecurityFilterChain { http { // ... headers { addHeaderWriter(XFrameOptionsHeaderWriter(XFrameOptionsMode.SAMEORIGIN)) } } return http.build() } }
At times, you may want to write a header only for certain requests.
For example, perhaps you want to protect only your login page from being framed.
You could use the DelegatingRequestMatcherHeaderWriter
to do so.
The following configuration example uses DelegatingRequestMatcherHeaderWriter
:
- Java
-
@Configuration @EnableWebSecurity public class WebSecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { RequestMatcher matcher = new AntPathRequestMatcher("/login"); DelegatingRequestMatcherHeaderWriter headerWriter = new DelegatingRequestMatcherHeaderWriter(matcher,new XFrameOptionsHeaderWriter()); http // ... .headers(headers -> headers .frameOptions(frameOptions -> frameOptions.disable()) .addHeaderWriter(headerWriter) ); return http.build(); } }
- XML
-
<http> <!-- ... --> <headers> <frame-options disabled="true"/> <header ref="headerWriter"/> </headers> </http> <beans:bean id="headerWriter" class="org.springframework.security.web.header.writers.DelegatingRequestMatcherHeaderWriter"> <beans:constructor-arg> <bean class="org.springframework.security.web.util.matcher.AntPathRequestMatcher" c:pattern="/login"/> </beans:constructor-arg> <beans:constructor-arg> <beans:bean class="org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter"/> </beans:constructor-arg> </beans:bean>
- Kotlin
-
@Configuration @EnableWebSecurity class SecurityConfig { @Bean open fun filterChain(http: HttpSecurity): SecurityFilterChain { val matcher: RequestMatcher = AntPathRequestMatcher("/login") val headerWriter = DelegatingRequestMatcherHeaderWriter(matcher, XFrameOptionsHeaderWriter()) http { headers { frameOptions { disable() } addHeaderWriter(headerWriter) } } return http.build() } }