Skip to main content

Middleware: Security Headers

Add security headers (CORS, HSTS, X-Frame-Options, etc.) to all responses using a Traefik headers Middleware.

Prerequisites​

base/base-values.yaml

image:
registry: docker.io
repository: traefik/whoami
tag: latest
port: 80

Configuration​

envs/prod/values.yaml

deployment:
enabled: true
replicas: 1

service:
enabled: true

customResources:
- |
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: security-headers
spec:
headers:
frameDeny: true
contentTypeNosniff: true
browserXssFilter: true
stsSeconds: 31536000
stsIncludeSubdomains: true
stsPreload: true
customResponseHeaders:
X-Custom-Header: "glueops-traefik"
accessControlAllowOriginList:
- "https://example.com"
accessControlAllowMethods:
- "GET"
- "POST"
- "OPTIONS"
accessControlAllowHeaders:
- "Content-Type"
- "Authorization"
accessControlMaxAge: 86400
- |
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: my-app-headers
annotations:
kubernetes.io/ingress.class: public-traefik
external-dns.alpha.kubernetes.io/target: public-v2.{{ .Values.captain_domain }}
spec:
entryPoints:
- web
- websecure
routes:
- match: Host(`{{ include "app.name" . }}.apps.{{ .Values.captain_domain }}`)
kind: Rule
middlewares:
- name: security-headers
services:
- name: '{{ include "app.name" . }}'
namespace: '{{ include "app.namespace" . }}'
port: 80

What Gets Created​

ResourceNamePurpose
Middlewaresecurity-headersAdds security response headers
IngressRoutemy-app-headersRoutes traffic through the middleware

Verify​

curl -I https://my-app-prod.apps.nonprod.antoniostacos.onglueops.com
note

The -prod suffix matches your environment folder name (envs/prod/). If you deploy to a different environment like envs/uat/, the suffix changes accordingly (e.g., my-app-uat).

Expected response headers:

X-Frame-Options: DENY
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-Custom-Header: glueops-traefik

Verify CORS​

CORS headers are only returned when the request includes an Origin header:

curl -I -H "Origin: https://example.com" https://my-app-prod.apps.nonprod.antoniostacos.onglueops.com

Expected additional response header:

Access-Control-Allow-Origin: https://example.com

A request with a non-allowed origin will not include the CORS header:

curl -I -H "Origin: https://evil.com" https://my-app-prod.apps.nonprod.antoniostacos.onglueops.com

Available Header Options​

OptionDescriptionExample
frameDenySets X-Frame-Options: DENYtrue
contentTypeNosniffSets X-Content-Type-Options: nosnifftrue
browserXssFilterSets X-XSS-Protection: 1; mode=blocktrue
stsSecondsSets Strict-Transport-Security max-age31536000
stsIncludeSubdomainsAdds includeSubDomains to HSTStrue
stsPreloadAdds preload to HSTStrue
customResponseHeadersAdd/override any response headerX-My-Header: value
accessControlAllowOriginListCORS allowed origins["https://example.com"]
accessControlAllowMethodsCORS allowed methods["GET", "POST"]
accessControlAllowHeadersCORS allowed headers["Content-Type"]
accessControlMaxAgeCORS preflight cache duration (seconds)86400

For the full list, see the Traefik headers middleware documentation.