[[servlet-headers]]
= Security HTTP Response Headers

You can use xref:features/exploits/headers.adoc#headers[Security HTTP Response Headers] to increase the security of web applications.
This section is dedicated to servlet-based support for Security HTTP Response Headers.

[[servlet-headers-default]]
== Default Security Headers

Spring Security provides a xref:features/exploits/headers.adoc#headers-default[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 <<servlet-headers-frame-options,X-Frame-Options>>.

You can do so with the following configuration:

.Customize Default Security Headers
====
.Java
[source,java,role="primary"]
----
@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
[source,xml,role="secondary"]
----
<http>
	<!-- ... -->

	<headers>
		<frame-options policy="SAMEORIGIN" />
	</headers>
</http>
----

.Kotlin
[source,kotlin,role="secondary"]
----
@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 xref:features/exploits/headers.adoc#headers-cache-control[Cache Control]:

.Customize Cache Control Headers
====
.Java
[source,java,role="primary"]
----
@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
[source,xml,role="secondary"]
----
<http>
	<!-- ... -->

	<headers defaults-disabled="true">
		<cache-control/>
	</headers>
</http>
----

.Kotlin
[source,kotlin,role="secondary"]
----
@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:

.Disable All HTTP Security Headers
====
.Java
[source,java,role="primary"]
----
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

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

.XML
[source,xml,role="secondary"]
----
<http>
	<!-- ... -->

	<headers disabled="true" />
</http>
----

.Kotlin
[source,kotlin,role="secondary"]
----
@Configuration
@EnableWebSecurity
class SecurityConfig {
    @Bean
    open fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http {
            // ...
            headers {
                disable()
            }
        }
        return http.build()
    }
}
----
====

[[servlet-headers-cache-control]]
== Cache Control

Spring Security includes xref:features/exploits/headers.adoc#headers-cache-control[Cache Control] headers by default.

However, if you actually want to cache specific responses, your application can selectively invoke https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletResponse.html#setHeader(java.lang.String,java.lang.String)[`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 https://docs.spring.io/spring/docs/5.0.0.RELEASE/spring-framework-reference/web.html#mvc-config-static-resources[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
[source,java,role="primary"]
----
@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
[source,xml,role="secondary"]
----
<http>
	<!-- ... -->

	<headers>
		<cache-control disabled="true"/>
	</headers>
</http>
----

.Kotlin
[source,kotlin,role="secondary"]
----
@Configuration
@EnableWebSecurity
class SecurityConfig {

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

[[servlet-headers-content-type-options]]
== Content Type Options

Spring Security includes xref:features/exploits/headers.adoc#headers-content-type-options[Content-Type] headers by default.
However, you can disable it:

.Content Type Options Disabled
====
.Java
[source,java,role="primary"]
----
@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
[source,xml,role="secondary"]
----
<http>
	<!-- ... -->

	<headers>
		<content-type-options disabled="true"/>
	</headers>
</http>
----

.Kotlin
[source,kotlin,role="secondary"]
----
@Configuration
@EnableWebSecurity
class SecurityConfig {

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

[[servlet-headers-hsts]]
== HTTP Strict Transport Security (HSTS)

By default, Spring Security provides the xref:features/exploits/headers.adoc#headers-hsts[Strict Transport Security] header.
However, you can explicitly customize the results.
The following example explicitly provides HSTS:

.Strict Transport Security
====
.Java
[source,java,role="primary"]
----
@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
[source,xml,role="secondary"]
----
<http>
	<!-- ... -->

	<headers>
		<hsts
			include-subdomains="true"
			max-age-seconds="31536000"
			preload="true" />
	</headers>
</http>
----

.Kotlin
[source,kotlin,role="secondary"]
----
@Configuration
@EnableWebSecurity
class SecurityConfig {

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

[[servlet-headers-hpkp]]
== HTTP Public Key Pinning (HPKP)
Spring Security provides servlet support for xref:features/exploits/headers.adoc#headers-hpkp[HTTP Public Key Pinning], but it is xref:features/exploits/headers.adoc#headers-hpkp-deprecated[no longer recommended].

You can enable HPKP headers with the following configuration:

.HTTP Public Key Pinning
====
.Java
[source,java,role="primary"]
----
@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
[source,xml,role="secondary"]
----
<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
[source,kotlin,role="secondary"]
----
@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()
    }
}
----
====

[[servlet-headers-frame-options]]
== X-Frame-Options

By default, Spring Security instructs browsers to block reflected XSS attacks by using the xref:features/exploits/headers.adoc#headers-frame-options[X-Frame-Options].

For example, the following configuration specifies that Spring Security should no longer instruct browsers to block the content:

.X-Frame-Options: SAMEORIGIN
====
.Java
[source,java,role="primary"]
----
@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
[source,xml,role="secondary"]
----
<http>
	<!-- ... -->

	<headers>
		<frame-options
		policy="SAMEORIGIN" />
	</headers>
</http>
----


.Kotlin
[source,kotlin,role="secondary"]
----
@Configuration
@EnableWebSecurity
class SecurityConfig {

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

[[servlet-headers-xss-protection]]
== X-XSS-Protection

By default, Spring Security instructs browsers to block reflected XSS attacks by 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
[source,java,role="primary"]
----
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

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

.XML
[source,xml,role="secondary"]
----
<http>
	<!-- ... -->

	<headers>
		<xss-protection block="false"/>
	</headers>
</http>
----

.Kotlin
[source,kotlin,role="secondary"]
----
@Configuration
@EnableWebSecurity
class SecurityConfig {

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

[[servlet-headers-csp]]
== Content Security Policy (CSP)

Spring Security does not add xref:features/exploits/headers.adoc#headers-csp[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 Example
====
[source,http]
----
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:

.Content Security Policy
====
.Java
[source,java,role="primary"]
----
@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
[source,xml,role="secondary"]
----
<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
[source,kotlin,role="secondary"]
----
@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:

.Content Security Policy Report Only
====
.Java
[source,java,role="primary"]
----
@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
[source,xml,role="secondary"]
----
<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
[source,kotlin,role="secondary"]
----
@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()
    }
}
----
====

[[servlet-headers-referrer]]
== Referrer Policy

Spring Security does not add xref:features/exploits/headers.adoc#headers-referrer[Referrer Policy] headers by default.
You can enable the Referrer Policy header by using the configuration:

.Referrer Policy
====
.Java
[source,java,role="primary"]
----
@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
[source,xml,role="secondary"]
----
<http>
	<!-- ... -->

	<headers>
		<referrer-policy policy="same-origin" />
	</headers>
</http>
----

.Kotlin
[source,kotlin,role="secondary"]
----
@Configuration
@EnableWebSecurity
class SecurityConfig {

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

[[servlet-headers-feature]]
== Feature Policy

Spring Security does not add xref:features/exploits/headers.adoc#headers-feature[Feature Policy] headers by default.
Consider the following `Feature-Policy` header:

.Feature-Policy Example
====
[source]
----
Feature-Policy: geolocation 'self'
----
====

You can enable the preceding feature policy header by using the following configuration:

.Feature-Policy
====
.Java
[source,java,role="primary"]
----
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

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

.XML
[source,xml,role="secondary"]
----
<http>
	<!-- ... -->

	<headers>
		<feature-policy policy-directives="geolocation 'self'" />
	</headers>
</http>
----

.Kotlin
[source,kotlin,role="secondary"]
----
@Configuration
@EnableWebSecurity
class SecurityConfig {

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

[[servlet-headers-permissions]]
== Permissions Policy

Spring Security does not add xref:features/exploits/headers.adoc#headers-permissions[Permissions Policy] headers by default.
Consider the following `Permissions-Policy` header:

.Permissions-Policy Example
====
[source]
----
Permissions-Policy: geolocation=(self)
----
====

You can enable the preceding permissions policy header using the following configuration:

.Permissions-Policy
====
.Java
[source,java,role="primary"]
----
@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
[source,xml,role="secondary"]
----
<http>
	<!-- ... -->

	<headers>
		<permissions-policy policy="geolocation=(self)" />
	</headers>
</http>
----

.Kotlin
[source,kotlin,role="secondary"]
----
@Configuration
@EnableWebSecurity
class SecurityConfig {

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

[[servlet-headers-clear-site-data]]
== Clear Site Data

Spring Security does not add xref:features/exploits/headers.adoc#headers-clear-site-data[Clear-Site-Data] headers by default.
Consider the following Clear-Site-Data header:

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

You can send the preceding header on log out with the following configuration:

.Clear-Site-Data
====
.Java
[source,java,role="primary"]
----
@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
[source,kotlin,role="secondary"]
----
@Configuration
@EnableWebSecurity
class SecurityConfig {

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

[[servlet-headers-custom]]
== 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.

[[servlet-headers-static]]
=== Static 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:

[source]
----
X-Custom-Security-Header: header-value
----

Given the preceding header, you could add the headers to the response by using the following configuration:

.StaticHeadersWriter
====
.Java
[source,java,role="primary"]
----
@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
[source,xml,role="secondary"]
----
<http>
	<!-- ... -->

	<headers>
		<header name="X-Custom-Security-Header" value="header-value"/>
	</headers>
</http>
----

.Kotlin
[source,kotlin,role="secondary"]
----
@Configuration
@EnableWebSecurity
class SecurityConfig {

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

[[servlet-headers-writer]]
=== 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`.

The next example use a custom instance of `XFrameOptionsHeaderWriter`.
If you wanted to explicitly configure <<servlet-headers-frame-options>>, you could do so with the following configuration:

.Headers Writer
====
.Java
[source,java,role="primary"]
----
@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
[source,xml,role="secondary"]
----
<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
[source,kotlin,role="secondary"]
----
@Configuration
@EnableWebSecurity
class SecurityConfig {

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

[[headers-delegatingrequestmatcherheaderwriter]]
=== DelegatingRequestMatcherHeaderWriter

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`:

.DelegatingRequestMatcherHeaderWriter Java Configuration
====
.Java
[source,java,role="primary"]
----
@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
[source,xml,role="secondary"]
----
<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
[source,kotlin,role="secondary"]
----
@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()
    }
}
----
====
