Global redirect www to non-www with HTTPS redirection

Good day,
I have applied a global HTTP -> HTTPS redirection using this this approach and it works fine. Additionally, I would like to add global www -> non-www redirection, which gets applied to any route.

This my static configuration:

# static configuration
[entryPoints]
   [entryPoints.web]
      address = ":80"

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

I have added additional router, middleware and noop service, so my configuration looks like this:

# dynamic configuration
[http.routers]
   [http.routers.redirect-to-https]
      entryPoints = ["web"]
      middlewares = ["https-redirect"]
      rule = "HostRegexp(`{host:.+}`)"
      service = "noop"

   [http.routers.redirect-to-nonwww]
      entryPoints = ["web-secure"]
      middlewares = ["nonwww-redirect"]
      rule = "HostRegexp(`{host:(www.+)}`)"
      service = "noop2"

[http.middlewares]
   [http.middlewares.https-redirect.redirectScheme]
      scheme = "https"
      permanent = true

   [http.middlewares.nonwww-redirect.redirectRegex]
      regex = "^https://www.(.*)"
      replacement = "https://$1"
      permanent = true

[http.services]
  [http.services.noop.loadBalancer]
    [[http.services.noop.loadBalancer.servers]]
      url = "http://192.168.0.1"

   [http.services.noop2.loadBalancer]
    [[http.services.noop2.loadBalancer.servers]]
      url = "http://192.168.0.2"

If I try this configuration, the the www -> non-www redirection does not work. However, if I try to apply the redirect middleware directly within a Docker service:

whoami:
    image: containous/whoami
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.entrypoints=web-secure"
      - "traefik.http.routers.whoami.rule=HostRegexp(`{host:(www.)?example.com}`)"
      - "traefik.http.routers.whoami.tls.certResolver=web"
      - "traefik.http.routers.whoami.middlewares=nonwww-redirect@file"

it does work. But still, this approach is not global.
First thing that occurred to me is the fact that the redirect-to-nonwww router gets a lower priority than any other application router because its rule is simply shorter. I tried setting the priority:

 [http.routers.redirect-to-nonwww]
      entryPoints = ["web-secure"]
      middlewares = ["nonwww-redirect"]
      rule = "HostRegexp(`{host:(www.+)}`)"
      service = "noop2"
      priority = 100

but with no success.
Is there a chance to apply both HTTPS redirection and www -> non-www redirection globally?
Thank you!

Hey @Dandry,
any news on this. Do you have a working global redirect you could share?

@Dan42 hi, no.
I still plug in the redirect middleware explicitly to every service I want the redirect for. Still hoping for someone from the dev team shedding some light here.

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