NGINX Ingress Controller 5.6.0 introduces HSTS as a Policy type for the VirtualServer CRD. This blog gives a quick overview of the feature with some deployment examples and tips on how to correctly configure this policy.
Across this blog, we’re focused on:
- How HSTS policy works in NGINX Ingress Controller.
- Where to attach it in VirtualServer.
What is HSTS?
HTTP Strict Transport Security (HSTS) is a security feature that tells browsers to only communicate with your site over HTTPS, even if the user types in “http://” or follows an insecure link. It helps prevent man-in-the-middle attacks and cookie hijacking by ensuring that all communication is encrypted.
When a browser receives an HSTS header from a site, it will remember to only use HTTPS for that site for a specified period of time. This means that even if a user tries to access the site via HTTP, the browser will automatically redirect them to HTTPS.
Why Use a Policy for HSTS?
Using a Policy resource for HSTS in NGINX Ingress Controller allows you to define the HSTS settings in a reusable and centralized way. Instead of configuring HSTS headers directly in each VirtualServer or VirtualServer Route resource, you can create a Policy that specifies the desired HSTS parameters and then attach that Policy to any VirtualServer that requires it.
How HSTS Policy Works in NGINX Ingress Controller
At a high level:
- Create a Policy resource with
spec.hsts. - Attach it to a VirtualServer, at
VirtualServer.spec.policies. - NGINX Ingress Controller generates the
Strict-Transport-Securityheader with the configured directives.
Example HSTS policy:
apiVersion: k8s.nginx.org/v1
kind: Policy
metadata:
name: hsts-policy
spec:
hsts:
maxAge: 31536000
includeSubDomains: false
behindProxy: false
preload: false
When includeSubDomains is enabled the policy extends to all subdomains of the host, so browsers will enforce HTTPS for api.example.com, app.example.com, and any other subdomain.
If behindProxy is disabled, TLS termination is required at the Ingress Controller. If behindProxy is enabled, the Ingress Controller will trust the X-Forwarded-Proto header to determine if the original request was over HTTPS.
Enabling preload signals that the domain should be included in browser HSTS preload lists, which enforce HTTPS on the very first visit before any header is received.
This requires includeSubDomains to be enabled and the maxAge to be at least 31536000 seconds (1 year).
Example VirtualServer Referencing an HSTS Policy
You can deploy the policy with the VirtualServer HSTS example on GitHub. Policies can only be applied at the server level. If applied at the route level, the Policy will be rejected.
This VirtualServer references the HSTS policy and the routes inherit that policy:
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
name: webapp
spec:
host: webapp.example.com
tls:
secret: tls-secret
policies:
- name: hsts-policy
upstreams:
- name: webapp
service: webapp-svc
port: 80
routes:
- path: /
action:
pass: webapp
Example Combining HSTS with Other Policies
You can deploy the HSTS policy alongside any other policy. For example, the following VirtualServer references both an HSTS policy at the spec level and a CORS policy at the route level. NGINX Ingress Controller ensures both policies work together without additional configuration, even when route-level policies add their own custom headers.
This VirtualServer references the HSTS policy at the spec level and a CORS policy at the route level:
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
name: webapp
spec:
host: webapp.example.com
tls:
secret: tls-secret
policies:
- name: hsts-policy
upstreams:
- name: webapp
service: webapp-svc
port: 80
routes:
- path: /
action:
pass: webapp
policies:
- name: cors-policy
Safely Removing HSTS
In order to safely remove HSTS, it is important to understand that deleting the policy does not clear the browser’s cached directive, and the browser will continue enforcing HTTPS until maxAge expires, which can lock users out if TLS is later removed.
To safely remove HSTS, you should:
Update the policy to set
maxAgeto0and apply the change:
apiVersion: k8s.nginx.org/v1
kind: Policy
metadata:
name: hsts-policy
spec:
hsts:
maxAge: 0
This tells browsers to immediately expire the cached HSTS directive.
2. Allow time for clients to receive the updated header. Any client that visits the site will pick up the maxAge=0 directive and will stop enforcing HTTPS.
3. Once the updated header has been served, remove the policy reference from the VirtualServer and delete the policy resource.
For more details on how HSTS expiration works in browsers, see the MDN documentation on HSTS expiration.
Important Behavior to Remember
- HSTS policy is spec-level only – referencing it in a route or subroute is rejected.
- Only one HSTS policy per VirtualServer is applied. If multiple HSTS policies are referenced on the same VirtualServer, the first one is used and subsequent ones are ignored.
- TLS must be enabled unless
behindProxyis enabled. - The generated
Strict-Transport-Securityheader is only sent on HTTPS responses. If the request is HTTP, the header is not sent.
You can find complete working examples on GitHub and more documentation in our docs:
- HSTS Policy docs
- VirtualServer HSTS example
- NGINX Ingress Controller Documentation
- NGINX Community Forum


