Define a middleware in your Traefik dynamic configuration that sets the Content-Security-Policy-Report-Only header. Using a YAML file:
# dynamic-config.yml
http:
middlewares:
csp-report-only:
headers:
customResponseHeaders:
Content-Security-Policy-Report-Only: "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; report-uri https://cspwarden.com/api/v1/csp-report/YOUR-DOMAIN-TOKEN/; report-to csp-endpoint"
# Companion header: maps the "csp-endpoint" group named by report-to to your report URL
Reporting-Endpoints: 'csp-endpoint="https://cspwarden.com/api/v1/csp-report/YOUR-DOMAIN-TOKEN/"'Ship both headers together: report-to names a reporting group (csp-endpoint), and the Reporting-Endpoints header maps that name to the actual HTTPS URL. The legacy report-uri keeps older browsers working.
Or using TOML:
# dynamic-config.toml
[http.middlewares.csp-report-only.headers]
[http.middlewares.csp-report-only.headers.customResponseHeaders]
Content-Security-Policy-Report-Only = "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; report-uri https://cspwarden.com/api/v1/csp-report/YOUR-DOMAIN-TOKEN/; report-to csp-endpoint"
Reporting-Endpoints = 'csp-endpoint="https://cspwarden.com/api/v1/csp-report/YOUR-DOMAIN-TOKEN/"'Apply the middleware to your router so the header is added to all responses:
http:
routers:
my-router:
rule: "Host(`yourdomain.com`)"
entryPoints:
- websecure
middlewares:
- csp-report-only
service: my-service
tls:
certResolver: letsencryptIf you manage Traefik with Docker Compose, you can configure the middleware directly via labels:
# docker-compose.yml
services:
my-app:
image: my-app:latest
labels:
- "traefik.enable=true"
- "traefik.http.routers.my-app.rule=Host(`yourdomain.com`)"
- "traefik.http.routers.my-app.entrypoints=websecure"
- "traefik.http.routers.my-app.tls.certresolver=letsencrypt"
- "traefik.http.routers.my-app.middlewares=csp-headers"
- "traefik.http.middlewares.csp-headers.headers.customresponseheaders.Content-Security-Policy-Report-Only=default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; report-uri https://cspwarden.com/api/v1/csp-report/YOUR-DOMAIN-TOKEN/; report-to csp-endpoint"
- 'traefik.http.middlewares.csp-headers.headers.customresponseheaders.Reporting-Endpoints=csp-endpoint="https://cspwarden.com/api/v1/csp-report/YOUR-DOMAIN-TOKEN/"'After reviewing violations in your CSP Warden dashboard, replace Content-Security-Policy-Report-Only with Content-Security-Policy in your middleware definition:
http:
middlewares:
csp-enforce:
headers:
customResponseHeaders:
Content-Security-Policy: "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; report-uri https://cspwarden.com/api/v1/csp-report/YOUR-DOMAIN-TOKEN/; report-to csp-endpoint"
Reporting-Endpoints: 'csp-endpoint="https://cspwarden.com/api/v1/csp-report/YOUR-DOMAIN-TOKEN/"' providers.file.filename points to your dynamic config.traefik.enable=true is set and the container is on the same Docker network as Traefik.