Help with traefik and cloudflare full strict ssl/tls setup

I have spent 3 days trying to get cloudflare full strict ssl/tls to work but have not been successful

so here i am coming for help and am confident you guys will bail me out

essentially this is for docker swarm setup and i have ssl working using self signed certificate and also got cloudflare to work with flexible ssl/tls but now i want to add to cloudflare origin certificates so i can setup full strict ssl/tls mode

here are my current docker swarm yaml config

services:

  traefik:
    image: "traefik:v3.2"
    ports:
      - target: 80
        published: 80
        mode: host
      - target: 443
        published: 443
        mode: host
    deploy:
      replicas: 2
      placement:
        constraints:
          - node.labels.node != server-a
      update_config:
        parallelism: 1
        delay: 1s
        order: stop-first
      labels:
        traefik.enable: "true"
        traefik.http.routers.traefik-https.service: api@internal
        traefik.http.services.traefik.loadbalancer.server.port: "8080"
        traefik.http.routers.traefik-http.entrypoints: http
        traefik.http.routers.traefik-http.rule: Host(`router.example.com`)
        traefik.http.routers.traefik-https.entrypoints: https
        traefik.http.routers.traefik-https.rule: Host(`router.example.com`)
        traefik.http.routers.traefik-https.tls: "true"
        traefik.http.routers.traefik-https.tls.certresolver: "default"
        traefik.http.routers.traefik-http.middlewares: traefik-http-redirect
        traefik.http.middlewares.traefik-http-redirect.redirectscheme.scheme: https
        traefik.http.middlewares.traefik-http-redirect.redirectscheme.permanent: 'true'

    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "/store/traefik/conf/dynamic.yaml:/etc/traefik/dynamic.yaml:ro"
      - "/store/traefik/certs:/etc/certs:ro"

    env_file:
      - .env.traefik
    command:
      - "--log.level=DEBUG"
      - "--api.insecure=true"
      - "--api.dashboard=true"
      - "--providers.docker=true"
      - "--providers.swarm=true"
      - "--providers.swarm.endpoint=unix:///var/run/docker.sock"
      - "--providers.swarm.exposedbydefault=false"
      - "--providers.file.filename=/etc/traefik/dynamic.yaml"
      - "--entrypoints.http.address=:80"
      - "--entrypoints.https.address=:443"
      - "--entrypoints.http.transport.respondingTimeouts.readTimeout=10m"
      - "--entrypoints.https.transport.respondingTimeouts.readTimeout=10m"

    networks:
      - secret

networks:
  secret:
    external: true

and here is content of dynamic.yaml

tls:
  certificates:
    - certFile: "/etc/certs/self-cert.pem"
      keyFile: "/etc/certs/self-key.pem"
      stores:
        - default
    - certFile: "/etc/certs/cloudflare-cert.pem"
      keyFile: "/etc/certs/cloudflare-key.pem"
      stores:
        - cloudflare

  stores:
    default:
      defaultCertificate:
        certFile: "/etc/certs/self-cert.pem"
        keyFile: "/etc/certs/self-key.pem"

the cloudflare origin certs were downloaded in pem format and added as named in the right paths as mounted to traefik and confirmed they are in traefik at specified paths

and here is how i added using cloudflare as the certresolver to use for the app i want to setup cloudflare full strict ssl/tls mode on

services:

  app-1:
    image: "app/app-1:latest"
    networks:
      - secret
    env_file:
      - .env.app-1
    deploy:
      mode: replicated
      replicas: 1
      placement:
        constraints:
          - node.labels.node != server-a
      update_config:
        parallelism: 1
        order: start-first
        monitor: 5s
      labels:
        traefik.enable: "true"
        traefik.docker.network: secret
        traefik.http.services.app-1.loadbalancer.server.port: "8080"
        traefik.http.routers.app-1-http.entrypoints: http
        traefik.http.routers.app-1-http.rule: Host(`app-1.example.com`)
        traefik.http.routers.app-1-https.entrypoints: https
        traefik.http.routers.app-1-https.rule: Host(`app-1.example.com`)
        traefik.http.routers.app-1-https.tls: "true"
        traefik.http.routers.app-1-https.tls.certresolver: "cloudflare"

networks:
  secret:
    external: true

but i notice as soon as i update the ssl mode from flexible to full strict on cloudflare, i am not able to access the app anymore and as soon as i enable the flexible back then am able to access

also i do not seem to see any traffic getting to the app at all when i enable the full strict, so nop way to even check logs or troubleshoot this

and since i have never setup cloudflare full strict before, i have no idea how to troubleshoot this properly

so am here to get help

i did watch this youtube video

which sets things up on nginx but this is traefik so am just kind of in shock why this is not working

another app that is using the default certresolver works fine so i know the setup is fine

services:

  app-2:
    image: "app/app-2:latest"
    networks:
      - secret
    env_file:
      - .env.app-2
    deploy:
      mode: replicated
      replicas: 1
      placement:
        constraints:
          - node.labels.node != server-a
      update_config:
        parallelism: 1
        order: start-first
        monitor: 5s
      labels:
        traefik.enable: "true"
        traefik.docker.network: secret
        traefik.http.services.app-2.loadbalancer.server.port: "8080"
        traefik.http.routers.app-2-http.entrypoints: http
        traefik.http.routers.app-2-http.rule: Host(`app-2.example.com`)
        traefik.http.routers.app-2-https.entrypoints: https
        traefik.http.routers.app-2-https.rule: Host(`app-2.example.com`)
        traefik.http.routers.app-2-https.tls: "true"
        traefik.http.routers.app-2-https.tls.certresolver: "default"

networks:
  secret:
    external: true

so what am i doing wrong and what do i need to do to fix this?

thanks in advance

Enable and check Traefik debug log (doc) and Traefik access log in JSON format (doc).

What is shown during failing requests?

i already have DEBUG log setup in my traefik yaml, did you see it?

also i mentioned nothing shows up in the log, its like request to cloudflare does not even get to the app-1 service, so nothing to really see in traefik logs

seems cloudflare is just not able to reach the app at all as soon as i enable the full strict

one question i have is, is my setup correct? is dynamic.yaml content correct?
i think first is to confirm is my setup is correct as that will be helpful so i know to move focus to cloudflare and routing to the app

thanks

So i am hitting the apps directly on traefik and i update the traefik log from DEBUG to INFO and now les noise and more real errors showing up. DEBUG is useless really for anyone reading this, tough to see anything, just too much junk and the real errors are msised

so i get these errors

2025-03-20T15:26:26Z ERR Router uses a nonexistent certificate resolver certificateResolver=default routerName=app-2-https@swarm

2025-03-20T15:26:26Z ERR Router uses a nonexistent certificate resolver certificateResolver=cloudflare routerName=app-1-https@swarm

so what is why am asking is my configuration correct?

it is saying even the default certresolver is nonexistent but yet it uses that cert to serve the internal app-2.example.com, which uses the defualt certresolver in the yaml file i posted in original post above

app-1.example.com is the one that should use the cloudflare certresolver

so again for anyone willing to help me, is my setup correct in the opening threa message?

my raefik.yaml, my dynamic.yaml and the yaml for app-1 and app-2?

You assign a certResolver (used for LetsEncrypt), which is not defined (doc).

When using TLS certs loaded in dynamic config file, you only need to enable TLS ("true") on entrypoint or router.

well so how do i define what certs to use by an app service when i have multiple of them then?

as you can see i have default and i have cloudflare

how do i differentiate which the app service should use if default or if cloudflare?

Traefik will automatically use the matching TLS cert for the domain.

I have 2 certificates for the same domain example.com , one is self signed and the other cloudflare

here are the configs, please check and help me find out what am doing wrong please

traefik.yaml

services:

  traefik:
    image: "traefik:v3.2"
    ports:
      - target: 80
        published: 80
        mode: host
      - target: 443
        published: 443
        mode: host
    deploy:
      replicas: 2
      placement:
        constraints:
          - node.labels.node != server-a
      update_config:
        parallelism: 1
        delay: 1s
        order: stop-first
      labels:
        traefik.enable: "true"
        traefik.http.routers.traefik-https.service: api@internal
        traefik.http.services.traefik.loadbalancer.server.port: "8080"
        traefik.http.routers.traefik-http.entrypoints: http
        traefik.http.routers.traefik-http.rule: Host(`router.example.com`)
        traefik.http.routers.traefik-https.entrypoints: https
        traefik.http.routers.traefik-https.rule: Host(`router.example.com`)
        traefik.http.routers.traefik-https.tls: "true"
        traefik.http.routers.traefik-https.tls.certresolver: "default"
        traefik.http.routers.traefik-http.middlewares: traefik-http-redirect
        traefik.http.middlewares.traefik-http-redirect.redirectscheme.scheme: https
        traefik.http.middlewares.traefik-http-redirect.redirectscheme.permanent: 'true'

    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "/store/traefik/conf/dynamic.yaml:/etc/traefik/dynamic.yaml:ro"
      - "/store/traefik/certs:/etc/certs:ro"

    env_file:
      - .env.traefik
    command:
      - "--log.level=DEBUG"
      - "--api.insecure=true"
      - "--api.dashboard=true"
      - "--providers.docker=true"
      - "--providers.swarm=true"
      - "--providers.swarm.endpoint=unix:///var/run/docker.sock"
      - "--providers.swarm.exposedbydefault=false"
      - "--providers.file.filename=/etc/traefik/dynamic.yaml"
      - "--entrypoints.http.address=:80"
      - "--entrypoints.https.address=:443"
      - "--entrypoints.http.transport.respondingTimeouts.readTimeout=10m"
      - "--entrypoints.https.transport.respondingTimeouts.readTimeout=10m"

    networks:
      - secret

networks:
  secret:
    external: true

dynamic.yaml

tls:
  certificates:
    - certFile: "/etc/certs/self-cert.pem"
      keyFile: "/etc/certs/self-key.pem"
      stores:
        - default
    - certFile: "/etc/certs/cloudflare-cert.pem"
      keyFile: "/etc/certs/cloudflare-key.pem"
      stores:
        - cloudflare

  stores:
    default:
      defaultCertificate:
        certFile: "/etc/certs/self-cert.pem"
        keyFile: "/etc/certs/self-key.pem"

app-1.yaml

services:

  app-1:
    image: "app/app-1:latest"
    networks:
      - secret
    env_file:
      - .env.app-1
    deploy:
      mode: replicated
      replicas: 1
      placement:
        constraints:
          - node.labels.node != server-a
      update_config:
        parallelism: 1
        order: start-first
        monitor: 5s
      labels:
        traefik.enable: "true"
        traefik.docker.network: secret
        traefik.http.services.app-1.loadbalancer.server.port: "8080"
        traefik.http.routers.app-1-http.entrypoints: http
        traefik.http.routers.app-1-http.rule: Host(`app-1.example.com`)
        traefik.http.routers.app-1-https.entrypoints: https
        traefik.http.routers.app-1-https.rule: Host(`app-1.example.com`)
        traefik.http.routers.app-1-https.tls: "true"
        traefik.http.routers.app-1-https.tls.certresolver: "cloudflare"

networks:
  secret:
    external: true

app-2.yaml

services:

  app-2:
    image: "app/app-2:latest"
    networks:
      - secret
    env_file:
      - .env.app-2
    deploy:
      mode: replicated
      replicas: 1
      placement:
        constraints:
          - node.labels.node != server-a
      update_config:
        parallelism: 1
        order: start-first
        monitor: 5s
      labels:
        traefik.enable: "true"
        traefik.docker.network: secret
        traefik.http.services.app-2.loadbalancer.server.port: "8080"
        traefik.http.routers.app-2-http.entrypoints: http
        traefik.http.routers.app-2-http.rule: Host(`app-2.example.com`)
        traefik.http.routers.app-2-https.entrypoints: https
        traefik.http.routers.app-2-https.rule: Host(`app-2.example.com`)
        traefik.http.routers.app-2-https.tls: "true"
        traefik.http.routers.app-2-https.tls.certresolver: "default"

networks:
  secret:
    external: true

is it supported? because am still having this issue
when i have Flexible everything works but when i have full strict then all hell breaks loose

i have the cloudflare certs files so why do i need to go through hell? why setting or config so i need for this to work???

when "Flexible" is enabled

$ curl -ILv https://app-1.example.com
* Host app-1.example.com:443 was resolved.
* IPv6: 2001:db8::1, 2001:db8::2, 2001:db8::3, 2001:db8::4, 2001:db8::5, 2001:db8::6, 2001:db8::7
* IPv4: 203.0.113.1, 203.0.113.2, 203.0.113.3, 203.0.113.4, 203.0.113.5, 203.0.113.6, 203.0.113.7
*   Trying [2001:db8::1]:443...
* Connected to app-1.example.com (2001:db8::1) port 443
* ALPN: curl offers h2,http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
*  CAfile: /etc/ssl/cert.pem
*  CApath: none
* (304) (IN), TLS handshake, Server hello (2):
* (304) (IN), TLS handshake, Unknown (8):
* (304) (IN), TLS handshake, Certificate (11):
* (304) (IN), TLS handshake, CERT verify (15):
* (304) (IN), TLS handshake, Finished (20):
* (304) (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / AEAD-AES256-GCM-SHA384 / [blank] / UNDEF
* ALPN: server accepted h2
* Server certificate:
*  subject: CN=example.com
*  start date: Jun 27 01:11:42 2025 GMT
*  expire date: Sep 25 02:09:54 2025 GMT
*  subjectAltName: host "app-1.example.com" matched cert's "*.example.com"
*  issuer: C=US; O=Fake CA; CN=FakeCA1
*  SSL certificate verify ok.
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://app-1.example.com/
* [HTTP/2] [1] [:method: HEAD]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: app-1.example.com]
* [HTTP/2] [1] [:path: /]
* [HTTP/2] [1] [user-agent: curl/8.6.0]
* [HTTP/2] [1] [accept: */*]
> HEAD / HTTP/2
> Host: app-1.example.com
> User-Agent: curl/8.6.0
> Accept: */*
> 
< HTTP/2 200 
HTTP/2 200 
< date: Sat, 12 Jul 2025 04:02:33 GMT
< content-type: text/html
< nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}
< etag: W/"67db6ebe-1d3"
< last-modified: Thu, 20 Mar 2025 01:26:22 GMT
< server: cloudflare
< vary: Accept-Encoding
< cf-cache-status: DYNAMIC
< report-to: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=FAKE_REPORT_PAYLOAD"}]}
< cf-ray: AAAABBBBCCCCDDDD-DFW
< alt-svc: h3=":443"; ma=86400

< 
* Connection #0 to host app-1.example.com left intact

when "Full Strict" is enabled

$ curl -ILv https://app-1.example.com
* Host app-1.example.com:443 was resolved.
* IPv6: 2001:db8::1, 2001:db8::2, 2001:db8::3, 2001:db8::4, 2001:db8::5, 2001:db8::6, 2001:db8::7
* IPv4: 203.0.113.1, 203.0.113.2, 203.0.113.3, 203.0.113.4, 203.0.113.5, 203.0.113.6, 203.0.113.7
*   Trying [2001:db8::1]:443...
* Connected to app-1.example.com (2001:db8::1) port 443
* ALPN: curl offers h2,http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
*  CAfile: /etc/ssl/cert.pem
*  CApath: none
* (304) (IN), TLS handshake, Server hello (2):
* (304) (IN), TLS handshake, Unknown (8):
* (304) (IN), TLS handshake, Certificate (11):
* (304) (IN), TLS handshake, CERT verify (15):
* (304) (IN), TLS handshake, Finished (20):
* (304) (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / AEAD-AES256-GCM-SHA384 / [blank] / UNDEF
* ALPN: server accepted h2
* Server certificate:
*  subject: CN=example.com
*  start date: Jun 27 01:11:42 2025 GMT
*  expire date: Sep 25 02:09:54 2025 GMT
*  subjectAltName: host "app-1.example.com" matched cert's "*.example.com"
*  issuer: C=US; O=Fake CA; CN=FakeCA1
*  SSL certificate verify ok.
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://app-1.example.com/
* [HTTP/2] [1] [:method: HEAD]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: app-1.example.com]
* [HTTP/2] [1] [:path: /]
* [HTTP/2] [1] [user-agent: curl/8.6.0]
* [HTTP/2] [1] [accept: */*]
> HEAD / HTTP/2
> Host: app-1.example.com
> User-Agent: curl/8.6.0
> Accept: */*
> 
< HTTP/2 526 
HTTP/2 526 
< date: Sat, 12 Jul 2025 04:04:44 GMT
< content-type: text/plain; charset=UTF-8
< content-length: 15
< cache-control: private, max-age=0, no-store, no-cache, must-revalidate, post-check=0, pre-check=0
< expires: Thu, 01 Jan 1970 00:00:01 GMT
< referrer-policy: same-origin
< x-frame-options: SAMEORIGIN
< server: cloudflare
< cf-ray: EEEEFFFFGGGGHHHH-DFW

< 
* Connection #0 to host app-1.example.com left intact

as soon as i have full strict enabled i get Invalid SSL certificate

I am having basically the same issue, only with the following details:

  1. I am using Origin Certificates from Cloudflare
  2. When using them with nginx, the setup works in both Full and Full (Strict) encryption mode
  3. When using them with Traefik, it works in Full mode. But in Full (Strict) mode I am getting Invalid SSL certificate error.

This is extremely confusing.

If Traefik should use the origin certificate, you need to set it up correctly.

Share your full Traefik static and dynamic config, and compose file(s) if used.

Hi my config is as follows:
compose file relevant sections:

myapp:
  labels:
    - "traefik.enable=true"
    - "traefik.http.routers.web.rule=Host(`*****`) && PathPrefix(`/`)"
    - "traefik.http.routers.web.entrypoints=websecure"
    - "traefik.http.routers.web.service=web"
    - "traefik.http.routers.web.tls=true"
    - "traefik.http.services.web.loadbalancer.server.port=3000"
    - "traefik.http.services.web.loadbalancer.healthCheck.path=/health_check"
    - "traefik.http.services.web.loadbalancer.healthCheck.interval=10s"
    - "traefik.http.services.web.loadbalancer.healthCheck.timeout=1s"
    - "traefik.http.middlewares.test-retry.retry.attempts=5"
    - "traefik.http.middlewares.test-retry.retry.initialinterval=200ms"

traefik:
  image: traefik:v3.5.2
  labels:
    - "traefik.http.routers.api.rule=Host(`localhost`)"
    - "traefik.http.routers.api.service=api@internal"
  ports:
    - "80:8880"
    - "443:8443"
    - "127.0.0.1:8080:8080"
  volumes:
    - /var/run/docker.sock:/var/run/docker.sock:ro
    - /mnt/myapp-data/ssl:/certs
  configs:
    - source: traefik.yml
      target: /traefik.yml
  restart: unless-stopped

traefik.yml:

      log:
        level: INFO

      accessLog: {}

      api:
        dashboard: true
        insecure: true

      entryPoints:
        web:
          address: ':8880'
          http:
            redirections:
              entryPoint:
                to: websecure
                scheme: https
        websecure:
          address: ":8443"

      tls:
        certificates:
          - certFile: /certs/server.cf.crt
            keyFile: /certs/server.cf.key

      providers:
        docker:
          endpoint: "unix:///var/run/docker.sock"
          exposedByDefault: false
          watch: true

Thanks!

tls as root element is Traefik dynamic config, it does not belong in static config file.

Create a dynamic config file with tls and load it in static config via providers.file.

Optionally set is as default certificate (doc):

tls:
  stores:
    default:
      defaultCertificate:
        certFile: path/to/cert.crt
        keyFile: path/to/cert.key
1 Like

@bluepuma77 Thanks a lot! This worked. I was unsure about dynamic configuration.