Guide to Securing Your Traefik Setup- part 2

Guide to Securing Your Traefik Setup

This guide hardens your current Traefik configuration.
Your setup uses one dynamic file: /etc/traefik/dynamic_config.yml.

Make changes step by step. Restart Traefik after. Test on SSL Server Test (Powered by Qualys SSL Labs) and https://securityheaders.com/.

Step 1: Add Secure Headers Middleware

Add headers for A+ score on securityheaders.com.

In dynamic file /etc/traefik/dynamic_config.yml, add to http.middlewares:

http:
  middlewares:
    redirect-to-https:
      redirectScheme:
        scheme: https

    secure-headers:
      headers:
        stsSeconds: 63072000  # 2 years
        stsIncludeSubdomains: true
        stsPreload: true
        forceSTSHeader: true
        frameDeny: true
        customFrameOptionsValue: "SAMEORIGIN"
        contentTypeNosniff: true
        browserXssFilter: true
        customBrowserXSSValue: "1; mode=block"
        referrerPolicy: "same-origin"
        permissionsPolicy: "camera=(), microphone=(), geolocation=()"
        customResponseHeaders:
          X-Robots-Tag: "none,noarchive,nosnippet,notranslate,noimageindex"
          X-Powered-By: ""
          Server: ""

This adds HSTS, blocks frames, removes server info.

Step 2: Apply Secure Headers to HTTPS Routers

Apply only on HTTPS.

In dynamic file, add middlewares: - secure-headers to each HTTPS router:

Example for next-router:

    next-router:
      rule: "Host(`{{.DashboardDomain}}`) && !PathPrefix(`/api/v1`)"
      service: next-service
      entryPoints:
        - websecure
      middlewares:
        - secure-headers
      tls:
        certResolver: letsencrypt

Do same for api-router and ws-router.

Your HTTP redirect router keeps redirect-to-https. Good.

Step 3: Harden TLS Settings

Force strong TLS. Good for A+ on SSL Labs.

In dynamic file, add at top level:

tls:
  options:
    default:
      minVersion: VersionTLS12
      maxVersion: VersionTLS13
      sniStrict: true
      cipherSuites:
        - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
        - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
        - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
        - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
        - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
        - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
      curvePreferences:
        - X25519
        - CurveP384
        - CurveP521

In static config, under entryPoints.websecure.http.tls:

    http:
      tls:
        options: default
        certResolver: letsencrypt

This applies to all HTTPS traffic.

Step 4: Fix insecureSkipVerify

Do not skip backend cert verify.

In static config, change or remove:

serversTransport:
  insecureSkipVerify: false  # Or remove line

Force verify backend certs. Use real certs on backends if needed.

Step 5: Remove Unneeded Timeouts (Optional)

You have respondingTimeouts: readTimeout: "30m".
Long timeouts risk DoS. Remove or lower if not needed.

In static config, under entryPoints.websecure.transport:

Remove or set lower like readTimeout: "10s".

Step 6: Switch to DNS Challenge (Recommended)

HTTP challenge needs port 80 open always.
DNS challenge better. Supports wildcards. No port open for renew.

In static config, change certificatesResolvers:

certificatesResolvers:
  letsencrypt:
    acme:
      email: "{{.LetsEncryptEmail}}"
      storage: "/letsencrypt/acme.json"
      dnsChallenge:
        provider: cloudflare  # Or your provider
        delayBeforeCheck: 60
        resolvers:
          - "1.1.1.1:53"
          - "1.0.0.1:53"

Add env vars or secrets for provider token.
Use scoped token.

Final Notes

  • Keep your TCP proxy protocol parts if needed.
  • Test changes. Check logs.
  • This setup gets A+ on tests.
  • For extra: Add rate limit middlewares or CrowdSec.

Restart Traefik. Your setup now secure.

Old Guild which is in-depth and comprehensive
Security and Performance Enhancements in Traefik Configuration in pangolin - Networking - HHF Technology Forums

3 Likes

Just wanted to say thanks for all that you’re doing for the community. Your Web apps and guides have been extremely helpful and in the past you personally helped me get my Pangolin instance working. Appreciate all that you do, keep it up!

All this stuff for someone that knows enough to set up but not enough to actually know best practice is great.

Thanks again!

1 Like

Is this applicable with MWM?
just not to add step 1 as already the middleware is defined in templates.yaml
step2: add headers_secure@file at top in middleware in HTTPS router except HTTP which will have redirect-to-https.
rest follow all other steps i-e 3, 4, 5 and 6.

1 Like

Hey, thanks for your good work! How exactly can I add the cloudflare token for the DNS challenge?

1 Like

In your compose file

    environment:
      - 'CF_DNS_API_TOKEN=${CF_DNS_API_TOKEN}'
    env_file: .env
1 Like

Thanks for the fast answer!

for me only this works:
http:
tls:
certResolver: letsencrypt
Is options: default important?

1 Like

I forgot to mention you have to adjust the traefik_config.yml (Got sick right after that post)

certificatesResolvers:
  letsencrypt:
    acme:
      dnsChallenge:
        provider: cloudflare
        delayBeforeCheck: 60s
        resolvers:
          - "1.1.1.1:53"
          - "8.8.8.8:53"
      email: example@email.tld
      storage: "/letsencrypt/acme.json"
      caServer: "https://acme-v02.api.letsencrypt.org/directory"

Adjust your traefik_config.yml also
for me only this works: http: tls: certResolver: letsencrypt Is options: default important?
Could you elaborate?

1 Like

When I add “options: default” I get an yaml error so probably parsing I think. Without it, it works. What does it change?

1 Like

where are you adding this “option: default”?

1 Like

Here I use this but without “options:”

1 Like

Hi, When I put that in, I get this error in Traefik.

{"level":"debug","time":"2026-01-21T00:40:12Z","caller":"github.com/traefik/traefik/v3/pkg/server/service/loadbalancer/wrr/wrr.go:176","message":"Service selected by WRR: https://100.89.128.4:45694"}
{"level":"error","error":"tls: failed to verify certificate: x509: cannot validate certificate for 100.89.128.4 because it doesn't contain any IP SANs","time":"2026-01-21T00:40:12Z","caller":"github.com/traefik/traefik/v3/pkg/proxy/httputil/proxy.go:119","message":"500 Internal Server Error"}
1 Like

does your backend have certs??

it depends on what you have named this section


if its named default then it will not give an error.

I had indeed forgotten to name it default, but kinda same error.

{"level":"debug","time":"2026-01-21T00:59:29Z","caller":"github.com/traefik/traefik/v3/pkg/server/service/loadbalancer/wrr/wrr.go:176","message":"Service selected by WRR: https://100.89.128.4:43920"}
{"level":"error","error":"tls: failed to verify certificate: x509: cannot validate certificate for 100.89.128.4 because it doesn't contain any IP SANs","time":"2026-01-21T00:59:29Z","caller":"github.com/traefik/traefik/v3/pkg/proxy/httputil/proxy.go:119","message":"500 Internal Server Error"}

I only followed your guide, so I don’t think so?

1 Like

if you select false then it will give you an error if you don’t have certs in your backend.

Do you have any guide or docs to achieve that please ? :folded_hands:

1 Like

Are there any settings for caching to adjust?

I found that my DNS resolving is very slow after applying these changes.

1 Like

Additionally, doing this security hardening has broken 2 things.

One is access to my Proxmox GUI, which gives me an “internal server error” message.

The other is Navidrome. It will let me log into the web UI for my server, but then give me a message saying “server communication error”.

Any ideas on how to fix this?

1 Like

All this setup are very niche. They depend on lot of factors. For both questions will say that please deploy minimum configuration at a time.