Global redirect www to non-www with HTTPS redirection

There are already quite a few threads on this. Here is one of them: A global http -> https redirection?

@ldez Is there a github issue for that, since you said, that you are aware / want to improve this?

1 Like

@ldez Thank you for this, someone came up with this workaround:

        # Global http to https redirect
        traefik.http.routers.http-catchall.rule: "hostregexp(`{host:.+}`)"
        traefik.http.routers.http-catchall.entrypoints: web
        traefik.http.routers.http-catchall.middlewares: redirect-to-https

It seems to work, any downsides of this solution?

It's my solution :smile: Global http to https redirect in v2

2 Likes

And an awesome solution it was :grin:!

On a side note, I noticed you created a posting on GItHub regarding (or someone did) and I didn't notice the CLI version on there. Should it be on there as well to make sure if they use it they post up both version, does it not make a difference or did I just completely miss it (highly likely)?

Thanks again.

To answer to original question, a global approach can be:

🐋 With the Docker provider:
version: '3.7'

services:
  traefik:
    image: traefik:v2.1.3
    command:
      - --entrypoints.web.address=:80
      - --entrypoints.websecure.address=:443
      - --providers.docker=true
      - --providers.docker.exposedbydefault=false
      - --log.level=INFO
    ports:
      - 80:80
      - 443:443
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    labels:
      traefik.enable: true

      # Global redirection: http to https
      traefik.http.routers.http-catchall.rule: HostRegexp(`{host:(www\.)?.+}`)
      traefik.http.routers.http-catchall.entrypoints: web
      traefik.http.routers.http-catchall.middlewares: wwwtohttps
      
      # Global redirection: https (www.) to https
      traefik.http.routers.wwwsecure-catchall.rule: HostRegexp(`{host:(www\.).+}`)
      traefik.http.routers.wwwsecure-catchall.entrypoints: websecure
      traefik.http.routers.wwwsecure-catchall.tls: true
      traefik.http.routers.wwwsecure-catchall.middlewares: wwwtohttps

      # middleware: http(s)://(www.) to  https://
      traefik.http.middlewares.wwwtohttps.redirectregex.regex: ^https?://(?:www\.)?(.+)
      traefik.http.middlewares.wwwtohttps.redirectregex.replacement: https://$${1}
      traefik.http.middlewares.wwwtohttps.redirectregex.permanent: true

  whoami:
    image: containous/whoami
    labels:
      traefik.enable: true
      traefik.http.routers.whoami.rule: Host(`whoami.localhost`)
      traefik.http.routers.whoami.entrypoints: websecure
      traefik.http.routers.whoami.tls: true

🗒️ With the File provider:
  • traefik.toml (static configuration)
[entryPoints.web]
  address = ":80"
[entryPoints.websecure]
  address = ":443"

[providers.file]
  directory = "/dyn/"

[log]
  level = "INFO"
  • /dyn/global_redirection.toml (dynamic configuration)
# Global redirection: http to https
[http.routers.http-catchall]
  rule = "HostRegexp(`{host:(www\\.)?.+}`)"
  entryPoints = ["web"]
  middlewares = ["wwwtohttps"]
  service = "noop"

# Global redirection: https (www.) to https
[http.routers.wwwsecure-catchall]
  rule = "HostRegexp(`{host:(www\\.).+}`)"
  entryPoints = ["websecure"]
  middlewares = ["wwwtohttps"]
  service = "noop"
  [http.routers.wwwsecure-catchall.tls]

# middleware: http(s)://(www.) to  https://
[http.middlewares.wwwtohttps.redirectregex]
  regex = "^https?://(?:www\\.)?(.+)"
  replacement = "https://${1}"
  permanent = true

# NOOP service
[http.services.noop]
  [[http.services.noop.loadBalancer.servers]]
    url = "http://192.168.0.1:666"


🚢 With the Kubernetes CRD provider:
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: http_catchall
  namespace: bar

spec:
  entryPoints:
    - web
  routes:
  - match: 'HostRegexp(`{host:(www\.)?.+}`)'
    kind: Rule
    middlewares:
    - name: wwwtohttps
    services:
    - name: foo-svc
      port: 666

---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: wwwsecure_catchall
  namespace: bar

spec:
  entryPoints:
    - websecure
  routes:
  - match: 'HostRegexp(`{host:(www\.).+}`)'
    kind: Rule
    middlewares:
    - name: wwwtohttps
    services:
    - name: foo-svc
      port: 666
  tls: {}

---
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: wwwtohttps
  namespace: bar

spec:
  redirectRegex:
    regex: '^https?://(?:www\.)?(.+)'
    replacement: 'https://${1}'
    permanent: true

Results:


🐋 FYI another way (less global) to do that:
version: '3.7'

services:
  traefik:
    image: traefik:v2.1.3
    command:
      - --entrypoints.web.address=:80
      - --entrypoints.websecure.address=:443
      - --providers.docker=true
      - --providers.docker.exposedbydefault=false
      - --log.level=INFO
    ports:
      - 80:80
      - 443:443
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    labels:
      traefik.enable: true

      # Global redirection: http to https
      traefik.http.routers.http_catchall.rule: HostRegexp(`{host:.+}`)
      traefik.http.routers.http_catchall.entrypoints: web
      traefik.http.routers.http_catchall.middlewares: tohttps

      # middleware: http:// to  https://
      traefik.http.middlewares.tohttps.redirectscheme.scheme: https
      traefik.http.middlewares.tohttps.redirectscheme.permanent: true

      # middleware: https://www. to  https://
      traefik.http.middlewares.trim_www.redirectregex.regex: ^https://www\.(.+)
      traefik.http.middlewares.trim_www.redirectregex.replacement: https://$${1}
      traefik.http.middlewares.trim_www.redirectregex.permanent: true

  whoami:
    image: containous/whoami
    labels:
      traefik.enable: true

      traefik.http.routers.whoami.rule: Host(`whoami.localhost`, `www.whoami.localhost`)
      traefik.http.routers.whoami.entrypoints: websecure
      traefik.http.routers.whoami.middlewares: trim_www
      traefik.http.routers.whoami.tls: true

Note: the redirections are made by routers, the routers are a part of the dynamic configuration, so a CLI flags version is not possible because flags handle the static configuration.

6 Likes

Hi Idez,

This is what I'm looking for as I have spent countless hours trying to figure out how to do the www to http redirect to no avail... EXCEPT... I can't get it to work. I'm close but having Let's Encrypt issues (at least that's what I think my issue is).

What's happening:

Not sure why when I enter https://www... it doesn't work. I'm assuming it's trying to check the certificate before it does a redirect (URL remains at https://www.traefik-whoami.creativesandbox.dev). Same result in both Safari and Firefox though warnings are worded differently.

I tried adding and extra rule to the whoami service so that it read "Host(traefik-whoami.creativesandbox.dev) || Host(https://www.traefik-whoami.creativesandbox.dev)" but that didn't make any difference... and now I'm out of ideas.

Maybe I'm searching for the wrong things but all my searches for www to non-www have not helped me (until you came along with this).

Thanks for all the work so far, here's hoping you can help with this last little bit. Current code below:

# Based on https://blog.containo.us/traefik-2-0-docker-101-fc2893944b9d
# Global redirection incl. www to https [Global redirect www to non-www with HTTPS redirection](https://community.containo.us/t/global-redirect-www-to-non-www-with-https-redirection/2313/9?u=mindgonemad)
# Swarm Mode [How to install Traefik 2.x on a Docker Swarm](https://creekorful.me/how-to-install-traefik-2-docker-swarm/)

version: "3.7"

services:
  traefik:
    image: traefik:2.0.2
    networks:
      - traefik-public
    command:
      - --entrypoints.web.address=:80
      - --entrypoints.websecure.address=:443
      - --providers.docker.exposedbydefault=false
      # Swarm
      - --providers.docker.swarmMode=true
      # Enables web UI and tells Traefik to listen to docker
      - --providers.docker
      - --api
      # Let's Encrypt
      - --certificatesresolvers.leresolver.acme.email=myemail@email.com
      - --certificatesresolvers.leresolver.acme.storage=/letsencrypt/acme.json
      - --certificatesresolvers.leresolver.acme.tlschallenge=true
      # Logging
      - --log.level=DEBUG # DEBUG, ERROR, INFO???
      - --log.filePath=/traefik.log
      - --log.format=json
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./letsencrypt:/letsencrypt
      - ./logs/traefik.log:/traefik.log
      # So that Traefik can listen to the Docker events
      - /var/run/docker.sock:/var/run/docker.sock:ro
    deploy:
      placement:
        constraints:
          - node.role == manager
      labels:
        traefik.enable: "true"
        # Dashboard
        traefik.http.routers.traefik.rule: "Host(`traefik.creativesandbox.dev`)"
        traefik.http.routers.traefik.service: api@internal
        traefik.http.routers.traefik.tls.certresolver: leresolver
        traefik.http.routers.traefik.entrypoints: websecure
        traefik.http.routers.traefik.middlewares: auth-traefik
        # Swarm Mode
        traefik.http.services.traefik.loadbalancer.server.port: 80
        # Basic Auth
        traefik.http.middlewares.auth-traefik.basicauth.users: "user:reallylongcodegoeshere"
        # Global redirection - http to https
        traefik.http.routers.http-catchall.rule: "hostregexp(`{host:(www\\.)?.+}`)"
        traefik.http.routers.http-catchall.entrypoints: web
        traefik.http.routers.http-catchall.middlewares: redirect-to-https
        # Global redirection - https://www to https
        traefik.http.routers.https-catchall.rule: "hostregexp(`{host:(www\\.).+}`)"
        traefik.http.routers.https-catchall.entrypoints: websecure
        traefik.http.routers.https-catchall.tls: "true"
        traefik.http.routers.https-catchall.middlewares: redirect-to-https
        # Middleware redirection
        traefik.http.middlewares.redirect-to-https.redirectregex.regex: "^https?://(?:www\\.)?(.+)"
        traefik.http.middlewares.redirect-to-https.redirectregex.replacement: "https://$${1}"
        traefik.http.middlewares.redirect-to-https.redirectregex.permanent: "true"
        #traefik.http.middlewares.redirect-to-https.redirectscheme.scheme: https

  whoami:
    image: containous/whoami:v1.3.0
    networks:
      - traefik-public
    deploy:
      labels:
        traefik.enable: "true"
        traefik.http.routers.whoami.rule: "Host(`traefik-whoami.creativesandbox.dev`)"
        traefik.http.routers.whoami.middlewares: auth-whoami
        traefik.http.routers.whoami.entrypoints: websecure
        traefik.http.routers.whoami.tls: "true"
        traefik.http.routers.whoami.tls.certresolver: leresolver
        # Swarm Mode
        traefik.http.services.whoami.loadbalancer.server.port: 80
        # Basic Auth
        traefik.http.middlewares.auth-whoami.basicauth.users: "user:reallylongcodegoeshere"

networks:
  traefik-public:
    external: true

You need to make sure that the cert matches all the domains https connection can come to. I do not see you configuring any sans so that could be the reason.

I'll be honest, I'm struggling to wrap my head around this. So far I have done everything use CLI (labels) but I only see examples for SAN using yaml or toml files? Does this mean I need to create now or those? And/or do I need to switch back to DNS challenge instead of TLS because I must say the DNS challenge was a pain in the proverbial as every time I created a service I had to wait for it to propagate (and remember too)?

CLI and labels is not the same. CLI refer to static configuration and labels refer to dynamic one, these are not to be mixed. I'm assuming you don't actually mean CLI, but labels.

The labels reference is here: https://docs.traefik.io/reference/dynamic-configuration/docker/ Search for tls.domains

Thanks. It took me ages to learn/wrap my head around SANs but managed to get it working with two additional lines per service.

traefik.http.routers.whoami.rule: "Host(`traefik-whoami.craighofman.art`)"
traefik.http.routers.whoami.tls.domains[0].main: traefik-whoami.craighofman.art
traefik.http.routers.whoami.tls.domains[0].sans: www.traefik-whoami.craighofman.art

Tried leaving out the main but that didn't work.

I experienced the same problem: https://www.domain.tld was not redirected to https://domain.tld. The other two global redirects worked.
In my case I could solve it with adding the wwwtohttps middleware to the whoami container as well:

services:
  whoami:
    image: containous/whoami
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.rule=Host(`domain.tld`, `www.domain.tld`)"
      - "traefik.http.routers.whoami.entrypoints=websecure"
      - "traefik.http.routers.whoami.tls.certresolver=httpchallenge"
      - "traefik.http.routers.whoami.middlewares=wwwtohttps@docker"
    networks:
      - traefik

networks:
  traefik:
    external:
      name: traefik_default

But this is probably not a recommended solution. I will have to get into SANs as well :sweat_smile:

Hi idez,
I have updated my .toml file but only https redirection is working and the remaining ones are not working.
My toml file:

[entryPoints]
  [entryPoints.web]
    address= ":80"
  [entryPoints.web-secure]
    address = ":443"
  [entryPoints.dashboard]
    address = ":8080"

[api]
dashboard=true
[certificatesResolvers.default.acme]
    email = "cbtu44@gmail.com"
    storage = "acme.json"
    [certificatesResolvers.default.acme.dnsChallenge]
        provider = "gcloud"
        delayBeforeCheck = 0
[providers.docker]
  exposedByDefault = false
  endpoint = "unix:///var/run/docker.sock"
  watch = "true"
  network = "proxy"

[traefik.http.routers.http-catchall]
  rule = "HostRegexp(`{host:(www\\.)?.+}`)"
  entryPoints = ["web"]
  middlewares = ["wwwtohttps"]

# Global redirection: https (www.) to https
[traefik.http.routers.wwwsecure-catchall]
  rule = "HostRegexp(`{host:(www\\.).+}`)"
  entryPoints = ["websecure"]
  middlewares = ["wwwtohttps"]
  [traefik.http.routers.wwwsecure-catchall.tls]

# middleware: http(s)://(www.) to  https://
[traefik.http.middlewares.wwwtohttps.redirectregex]
  regex = "^https?://(?:www\\.)?(.+)"
  replacement = "https://${1}"
  permanent = true

in the v2, the dynamic configuration and the static configuration must be defined in separated files:

and like in the v1 the file provider must be enabled to use dynamic configuration from file.

1 Like

@ldez have defined the dynamic config in docker-compose file.
I am sorry if i didn't understand your question.

version: '3.3'
services:
  traefik:
    image: traefik:v2.0
    restart: always
    ports:
      - 80:80
      - 8080:8080
      - 443:443
    networks:
      - proxy
      - internal
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - $PWD/traefik.toml:/traefik.toml
      - $PWD/acme.json:/acme.json
    container_name: traefik
    labels:
      - traefik.enable=true
      #- traefik.http.routers.web.rule=Host(`stats.prision-aiop.com`)
      #- traefik.http.routers.web.entrypoints=web
      #- traefik.http.middlewares.web-https-redirects.redirectscheme.scheme=https
      #- traefik.http.routers.web.middlewares=web-https-redirects
      #- traefik.http.middlewares.web-https-redirects.redirectscheme.permanent=true
      - traefik.http.routers.api.entrypoints=web-secure
      - traefik.http.routers.api.rule=Host(`stats.domain.com`,`www.stats.domain.com`)
      - traefik.http.routers.api.service=api@internal
      - traefik.http.routers.api.middlewares=auth
      - traefik.http.middlewares.auth.basicauth.users=codebreaker:$$2y$$n6
      - traefik.http.routers.api.tls=true
      - traefik.http.routers.api.tls.certresolver=default
      - traefik.http.routers.api.tls.domains[0].main=domain.com
      - traefik.http.routers.api.tls.domains[0].sans=*.domain.com
      - traefik.http.routers.api.tls.domains[1].main=domain1.com
      - traefik.http.routers.api.tls.domains[1].sans=*.domain1.com
      - traefik.docker.network=proxy
      #- traefik.http.routers.api.middlewares=wwwtohttps
      # global redirections http to https
      - "traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)"
      - "traefik.http.routers.http-catchall.entrypoints=web"
      - "traefik.http.routers.http-catchall.middlewares=web-https-redirect"
       # Global redirection: https (www.) to https
      - "traefik.http.routers.wwwsecure-catchall.rule=hostregexp(`{host:(www\\.).+}`)"
      - "traefik.http.routers.wwwsecure-catchall.entrypoints=web-secure"
      - "traefik.http.routers.wwwsecure-catchall.tls=true"
      - "traefik.http.routers.wwwsecure-catchall.middlewares=web-https-redirect"
      # middleware: http(s)://(www.) to  https://
      - "traefik.http.middlewares.web-https-redirect.redirectregex.regex=^https?://(?:www\\.)?(.+)"
      - "traefik.http.middlewares.web-https-redirect.redirectregex.replacement=https://$${1}"
      - "traefik.http.middlewares.web-https-redirect.redirectregex.permanent=true"
1 Like

I updated/edited my previous answer:

@ldez can you post an example of yaml that would work in kubernetes. Mostly docker examples. Need kubernetes example

i have been back and forth trying to get @ldez solution to work but to no avail

i am using labels in two docker compose files, a main traefik file that also defines the dashboard.${DOMAIN}

the http -> https redirection for dashboard works fine

when i add lighttpd to handle the host={DOMAIN} , www.{DOMAIN} the strip www part doesn't work, it does strip it and redirect but i get a connection error

is it possible to get a complete example vs having snippets?

thanks for the help

I provided complete examples in my answer, you just have to click on items:

Hello @ldez, I tried to implement this improvement to include www to non-www redirecting on top of my http to https redirect using your previous method (that worked perfectly)

      # global redirect to https
      - "traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)"
      - "traefik.http.routers.http-catchall.entrypoints=web"
      - "traefik.http.routers.http-catchall.middlewares=redirect-to-https"

      # middleware redirect
      - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"

Unfortunately, after replacing it with the code for docker provider (both the global and less global approach) to my docker-compose file, the opposite thing happened and when surfing to my domain.eu I got redirected to www.domain.eu

I reverted to the previous situation and had all the trouble in hell to remove the permanent redirects from browser caches. Do you have an idea as to why I got the reversed redirect?

Also, can we set the "redirectregex.permanent: true" to false (or something else), in order to test/debug without using 301 redirects ?