This version is still in development and is not considered stable yet. For the latest stable version, please use Spring Security 6.1.12!

Security HTTP Response Headers

Security HTTP Response Headers can be used to increase the security of web applications. This section is dedicated to servlet based support for Security HTTP Response Headers.

Default Security 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 utilize the headers, so additional testing is encouraged.

You can customize specific headers. For example, assume that you want the defaults except you wish to specify SAMEORIGIN for X-Frame-Options.

You can easily do this with the following Configuration:

Customize Default Security Headers
  • Java

  • XML

  • Kotlin

@EnableWebSecurity
public class WebSecurityConfig {

	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			// ...
			.headers(headers -> headers
				.frameOptions(frameOptions -> frameOptions
					.sameOrigin()
				)
			);
		return http.build();
	}
}
<http>
	<!-- ... -->

	<headers>
		<frame-options policy="SAMEORIGIN" />
	</headers>
</http>
@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. An example is provided below:

If you are using Spring Security’s Configuration the following will only add Cache Control.

Customize Cache Control Headers
  • Java

  • XML

  • Kotlin

@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();
	}
}
<http>
	<!-- ... -->

	<headers defaults-disabled="true">
		<cache-control/>
	</headers>
</http>
@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:

Disable All HTTP Security Headers
  • Java

  • XML

  • Kotlin

@EnableWebSecurity
public class WebSecurityConfig {

	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			// ...
			.headers(headers -> headers.disable());
		return http.build();
	}
}
<http>
	<!-- ... -->

	<headers disabled="true" />
</http>
@EnableWebSecurity
class SecurityConfig {
    @Bean
    open fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http {
            // ...
            headers {
                disable()
            }
        }
        return http.build()
    }
}

Cache Control

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. This is useful to ensure things like CSS, JavaScript, and images are properly cached.

When using Spring Web MVC, this is typically done within your configuration. Details on how to do this can be found in the Static Resources portion of the Spring Reference documentation

If necessary, you can also disable Spring Security’s cache control HTTP response headers.

Cache Control Disabled
  • Java

  • XML

  • Kotlin

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			// ...
			.headers(headers -> headers
				.cacheControl(cache -> cache.disable())
			);
		return http.build();
	}
}
<http>
	<!-- ... -->

	<headers>
		<cache-control disabled="true"/>
	</headers>
</http>
@EnableWebSecurity
class SecurityConfig {

    @Bean
    open fun filterChain(http: HttpSecurity): SecurityFilterChain {
       http {
            headers {
                cacheControl {
                    disable()
                }
            }
        }
        return http.build()
    }
}

Content Type Options

Spring Security includes Content-Type headers by default. However, you can disable it with:

Content Type Options Disabled
  • Java

  • XML

  • Kotlin

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			// ...
			.headers(headers -> headers
				.contentTypeOptions(contentTypeOptions -> contentTypeOptions.disable())
			);
		return http.build();
	}
}
<http>
	<!-- ... -->

	<headers>
		<content-type-options disabled="true"/>
	</headers>
</http>
@EnableWebSecurity
class SecurityConfig {

    @Bean
    open fun filterChain(http: HttpSecurity): SecurityFilterChain {
       http {
            headers {
                contentTypeOptions {
                    disable()
                }
            }
        }
        return http.build()
    }
}

HTTP Strict Transport Security (HSTS)

Spring Security provides the Strict Transport Security header by default. However, you can customize the results explicitly. For example, the following is an example of explicitly providing HSTS:

Strict Transport Security
  • Java

  • XML

  • Kotlin

@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();
	}
}
<http>
	<!-- ... -->

	<headers>
		<hsts
			include-subdomains="true"
			max-age-seconds="31536000"
			preload="true" />
	</headers>
</http>
@EnableWebSecurity
class SecurityConfig {

    @Bean
    open fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http {
            headers {
                httpStrictTransportSecurity {
                    includeSubDomains = true
                    preload = true
                    maxAgeInSeconds = 31536000
                }
            }
        }
        return http.build()
    }
}

HTTP Public Key Pinning (HPKP)

For passivity reasons, 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:

HTTP Public Key Pinning
  • Java

  • XML

  • Kotlin

@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();
	}
}
<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>
@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()
    }
}

X-Frame-Options

By default, Spring Security disables rendering within an iframe using X-Frame-Options.

You can customize frame options to use the same origin within a Configuration using the following:

X-Frame-Options: SAMEORIGIN
  • Java

  • XML

  • Kotlin

@EnableWebSecurity
public class WebSecurityConfig {

	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			// ...
			.headers(headers -> headers
				.frameOptions(frameOptions -> frameOptions
					.sameOrigin()
				)
			);
		return http.build();
	}
}
<http>
	<!-- ... -->

	<headers>
		<frame-options
		policy="SAMEORIGIN" />
	</headers>
</http>
@EnableWebSecurity
class SecurityConfig {

    @Bean
    open fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http {
            headers {
                frameOptions {
                    sameOrigin = true
                }
            }
        }
        return http.build()
    }
}

X-XSS-Protection

By default, Spring Security instructs browsers to block reflected XSS attacks using the <<headers-xss-protection,X-XSS-Protection header>. However, you can change this default. For example, the following Configuration specifies that Spring Security should no longer instruct browsers to block the content:

X-XSS-Protection Customization
  • Java

  • XML

  • Kotlin

@EnableWebSecurity
public class WebSecurityConfig {

	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			// ...
			.headers(headers -> headers
				.xssProtection(xss -> xss
					.block(false)
				)
			);
		return http.build();
	}
}
<http>
	<!-- ... -->

	<headers>
		<xss-protection block="false"/>
	</headers>
</http>
@EnableWebSecurity
class SecurityConfig {

    @Bean
    open fun filterChain(http: HttpSecurity): SecurityFilterChain {
        // ...
        http {
            headers {
                xssProtection {
                    block = false
                }
            }
        }
        return http.build()
    }
}

Content Security Policy (CSP)

Spring Security does not add Content Security Policy by default, because a reasonable default is impossible to know without context of the application. The web application author must declare the security policy(s) to enforce and/or monitor for the protected resources.

For example, given the following security policy:

Content Security Policy Example
Content-Security-Policy: script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/

You can enable the CSP header as shown below:

Content Security Policy
  • Java

  • XML

  • Kotlin

@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();
	}
}
<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>
@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:

Content Security Policy Report Only
  • Java

  • XML

  • Kotlin

@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();
	}
}
<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>
@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()
    }
}

Referrer Policy

Spring Security does not add Referrer Policy headers by default. You can enable the Referrer Policy header using the configuration as shown below:

Referrer Policy
  • Java

  • XML

  • Kotlin

@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();
	}
}
<http>
	<!-- ... -->

	<headers>
		<referrer-policy policy="same-origin" />
	</headers>
</http>
@EnableWebSecurity
class SecurityConfig {

    @Bean
    open fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http {
            // ...
            headers {
                referrerPolicy {
                    policy = ReferrerPolicy.SAME_ORIGIN
                }
            }
        }
        return http.build()
    }
}

Feature Policy

Spring Security does not add Feature Policy headers by default. The following Feature-Policy header:

Feature-Policy Example
Feature-Policy: geolocation 'self'

can enable the Feature Policy header using the configuration shown below:

Feature-Policy
  • Java

  • XML

  • Kotlin

@EnableWebSecurity
public class WebSecurityConfig {

	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			// ...
			.headers(headers -> headers
				.featurePolicy("geolocation 'self'")
			);
		return http.build();
	}
}
<http>
	<!-- ... -->

	<headers>
		<feature-policy policy-directives="geolocation 'self'" />
	</headers>
</http>
@EnableWebSecurity
class SecurityConfig {

    @Bean
    open fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http {
            // ...
            headers {
                featurePolicy("geolocation 'self'")
            }
        }
        return http.build()
    }
}

Permissions Policy

Spring Security does not add Permissions Policy headers by default. The following Permissions-Policy header:

Permissions-Policy Example
Permissions-Policy: geolocation=(self)

can enable the Permissions Policy header using the configuration shown below:

Permissions-Policy
  • Java

  • XML

  • Kotlin

@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();
	}
}
<http>
	<!-- ... -->

	<headers>
		<permissions-policy policy="geolocation=(self)" />
	</headers>
</http>
@EnableWebSecurity
class SecurityConfig {

    @Bean
    open fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http {
            // ...
            headers {
                permissionPolicy {
                    policy = "geolocation=(self)"
                }
            }
        }
        return http.build()
    }
}

Clear Site Data

Spring Security does not add Clear-Site-Data headers by default. The following Clear-Site-Data header:

Clear-Site-Data Example
Clear-Site-Data: "cache", "cookies"

can be sent on log out with the following configuration:

Clear-Site-Data
  • Java

  • Kotlin

@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();
	}
}
@EnableWebSecurity
class SecurityConfig {

    @Bean
    open fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http {
            // ...
            logout {
                addLogoutHandler(HeaderWriterLogoutHandler(ClearSiteDataHeaderWriter(CACHE, COOKIES)))
            }
        }
        return http.build()
    }
}

Cross-Origin Policies

Spring Security provides built-in support for adding some Cross-Origin policies headers, those headers are:

Cross-Origin-Opener-Policy
Cross-Origin-Embedder-Policy
Cross-Origin-Resource-Policy

Spring Security does not add Cross-Origin Policies headers by default. The headers can be added with the following configuration:

Cross-Origin Policies
  • Java

  • Kotlin

@EnableWebSecurity
public class WebSecurityConfig {

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) {
        http.headers((headers) -> headers
                .crossOriginOpenerPolicy(CrossOriginOpenerPolicy.SAME_ORIGIN)
                .crossOriginEmbedderPolicy(CrossOriginEmbedderPolicy.REQUIRE_CORP)
                .crossOriginResourcePolicy(CrossOriginResourcePolicy.SAME_ORIGIN)));
        return http.build();
    }
}
@EnableWebSecurity
open class CrossOriginPoliciesConfig {
    @Bean
    open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
        http {
            headers {
                crossOriginOpenerPolicy(CrossOriginOpenerPolicy.SAME_ORIGIN)
                crossOriginEmbedderPolicy(CrossOriginEmbedderPolicy.REQUIRE_CORP)
                crossOriginResourcePolicy(CrossOriginResourcePolicy.SAME_ORIGIN)
            }
        }
        return http.build()
    }
}

This configuration will write the headers with the values provided:

Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Resource-Policy: same-origin

Custom Headers

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.

Static Headers

There may be times you wish to inject custom security headers into your application that are not supported out of the box. For example, given the following custom security header:

X-Custom-Security-Header: header-value

The headers could be added to the response using the following Configuration:

StaticHeadersWriter
  • Java

  • XML

  • Kotlin

@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();
	}
}
<http>
	<!-- ... -->

	<headers>
		<header name="X-Custom-Security-Header" value="header-value"/>
	</headers>
</http>
@EnableWebSecurity
class SecurityConfig {

    @Bean
    open fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http {
            // ...
            headers {
                addHeaderWriter(StaticHeadersWriter("X-Custom-Security-Header","header-value"))
            }
        }
        return http.build()
    }
}

Headers Writer

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.

Let’s take a look at an example of using an custom instance of XFrameOptionsHeaderWriter. If you wanted to explicitly configure X-Frame-Options it could be done with the following Configuration:

Headers Writer
  • Java

  • XML

  • Kotlin

@EnableWebSecurity
public class WebSecurityConfig {

	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			// ...
			.headers(headers -> headers
				.addHeaderWriter(new XFrameOptionsHeaderWriter(XFrameOptionsMode.SAMEORIGIN))
			);
		return http.build();
	}
}
<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"/>
@EnableWebSecurity
class SecurityConfig {

    @Bean
    open fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http {
            // ...
            headers {
                addHeaderWriter(XFrameOptionsHeaderWriter(XFrameOptionsMode.SAMEORIGIN))
            }
        }
        return http.build()
    }
}

DelegatingRequestMatcherHeaderWriter

At times you may want to only write a header for certain requests. For example, perhaps you want to only protect your log in page from being framed. You could use the DelegatingRequestMatcherHeaderWriter to do so.

An example of using DelegatingRequestMatcherHeaderWriter in Java Configuration can be seen below:

DelegatingRequestMatcherHeaderWriter Java Configuration
  • Java

  • XML

  • Kotlin

@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();
	}
}
<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>
@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()
    }
}