Global redirect www to non-www with HTTPS redirection

After release of Traefik v2.2 I guess the right approach to make http to https redirection will be:

## Static configuration
entryPoints:
  web:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https

  websecure:
    address: ":443"

Docker www to non-www redirection:

labels:
      # Global redirection: https (www.) to https
      traefik.http.routers.websecure-catchall.rule: HostRegexp(`{host:(www\.).+}`)
      traefik.http.routers.websecure-catchall.entrypoints: websecure
      traefik.http.routers.websecure-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

But do we need to add acme resolver for www SSL certificate?

      traefik.http.routers.websecure-catchall.tls: true
      traefik.http.routers.websecure-catchall.tls.certresolver: myresolver

For anyone who is looking for a solution, to determine per domain whether it should be www or without www. I made 2 middlewares, which can be set per domain. I was looking after this. but couldn't find a quick answer. That's why i'm sharing my solution here:

Part of my global traefik labels (v2.1)

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

        # middleware redirect
        - traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https
        - traefik.http.middlewares.redirect-to-https.redirectscheme.permanent=true
        
        # middleware redirect to www
        - traefik.http.middlewares.redirect-to-www.redirectregex.regex=(https|http)://(?:www.)?(.*)
        - traefik.http.middlewares.redirect-to-www.redirectregex.replacement=https://www.$${2}
        
        # middleware redirect to non-www
        - traefik.http.middlewares.redirect-to-nonwww.redirectregex.regex=(https|http)://(www\.(.*))
        - traefik.http.middlewares.redirect-to-nonwww.redirectregex.replacement=https://$${3}

In a website for www. -> redirect to https://www.example.com

labels:
        - traefik.enable=true
        - traefik.http.services.${SERVICE}-app.loadbalancer.server.port=80
        - traefik.http.routers.${SERVICE}-app.rule=host(`example.com`,`www.example.com`)
        - traefik.http.routers.${SERVICE}-app.entrypoints=https
        - traefik.http.routers.${SERVICE}-app.tls=true
        - traefik.http.routers.${SERVICE}-app.tls.certresolver=letsencrypt
        - traefik.http.routers.${SERVICE}-app.service=${SERVICE}-app
        - traefik.http.routers.${SERVICE}-app.middlewares=redirect-to-www

In a website for no www -> redirect to https://example.com

labels:
        - traefik.enable=true
        - traefik.http.services.${SERVICE}-app.loadbalancer.server.port=80
        - traefik.http.routers.${SERVICE}-app.rule=host(`example.com`,`www.example.com`)
        - traefik.http.routers.${SERVICE}-app.entrypoints=https
        - traefik.http.routers.${SERVICE}-app.tls=true
        - traefik.http.routers.${SERVICE}-app.tls.certresolver=letsencrypt
        - traefik.http.routers.${SERVICE}-app.service=${SERVICE}-app
        - traefik.http.routers.${SERVICE}-app.middlewares=redirect-to-nonwww

you can also use the solution in my previous post

Your solution certainly looks good too. And is the answer to the original post. My swarm environment has multiple domains, with different wishes to www or non www and don't want people to go to the wrong url. But they will been forced to the correct www or non www environment.

Examples

Force to www environment

- traefik.http.routers.${SERVICE}-app.middlewares=redirect-to-www

Force to the non-www environment

- traefik.http.routers.${SERVICE}-app.middlewares=redirect-to-nonwww

I realize now that the placement of my solution was not completely fitting to question of the tropic starter

A word of note:
If you plan on submitting your domain to HSTS preload a single redirect will not work and is against the standard of HSTS.

And in my testing, redirects don't support headers (or didn't before).

1 Like

Hey mate - great work on the solution.

I found the regex in your KubernetesCRD to not work, I had to change
^https?://(?:www\.)?(.+)
To
^https?:\/\/(?:www\.)?(.+)

But otherwise all is well.

Hey,
Where are you setting the labels in the kubernetesCRD setup?
In the deployment YAML or as part of an IngressRoute?

Would be great if you could post a more complete yml example of what you did.

Thanks!

Big thanks for the code! But... why this? [http.routers.wwwsecure-catchall.tls]

Big thanks for the code! But... why this? [http.routers.wwwsecure-catchall.tls]

Could be because traefik 2.2 was not there back then.

In my case (w/ Traefik 2.2.5), without:

traefik.http.routers.wwwsecure-catchall.tls: true

The rule https://www.whoami.localhost/ -> https://whoami.localhost is broken. Details:

      # Global redirection: http to https
      traefik.http.routers.http-catchall.rule: HostRegexp(`{host:(www\.)?.+}`)
      traefik.http.routers.http-catchall.entrypoints: webinsecure
      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 # I need this!
      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

@floatingpurr not really surprising, right? You are not using the entry point redirection at all in that config.

Yep :slight_smile: but I did not get your post where you say:

Could be because traefik 2.2 was not there back then.

Isn't it a valid configuration for Traefik v.2.2.x?

I was referring to the fact that the entry point redirection was added in that version.

1 Like

Oh, I wasn't aware of that!

Please, don't hate me :stuck_out_tongue: Just to grasp better how v.2 works under the hood, I'm trying to mix redirections up and I moved http > https rule at the entry point level. This is my example:

command:
      - --entrypoints.webinsecure.address=:80
      - --entrypoints.webinsecure.http.redirections.entrypoint.to=websecure
      - --entrypoints.webinsecure.http.redirections.entrypoint.scheme=https
      - --entrypoints.websecure.address=:443
labels:
      # Global redirection: http to https (no more necessary now)
      # traefik.http.routers.http-catchall.rule: HostRegexp(`{host:(www\.)?.+}`)
      # traefik.http.routers.http-catchall.entrypoints: webinsecure
      # 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

The redirection chain is:
http://www.foo.com -> https://www.foo.com -> https://foo.com

However, If I remove the option traefik.http.routers.wwwsecure-catchall.tls, I get:
http://www.foo.com -> https://www.foo.com but now I get a 404 for https://www.foo.com

Shouldn't wwwsecure-catchall take care of https://www.foo.com regardless of that option?

Sorry, I do not understand. No? Why do you expect it to? 404 means that no rules match and indeed in this case you won’t have a tls router with a matching rule.

Sorry, my bad. I do not want to take advantage of your time. The thing is that from these previous posts:

Big thanks for the code! But... why this? [http.routers.wwwsecure-catchall.tls]

Could be because traefik 2.2 was not there back then.

Isn't it a valid configuration for Traefik v.2.2.x?

I was referring to the fact that the entry point redirection was added in that version

I erroneously understood that http.routers.wwwsecure-catchall.tls could have been avoided when you use the entry point redirection... but it is exactly the contrary.

As the doc says, you need to explicitly dedicate the router to HTTPS. So, my example could work iif traefik.http.routers.wwwsecure-catchall.tls is there.

Yes! It could! you do not need the middleware for redirecting from http to https, if you apply entry point redirection. But in your last example when you get 404 at https://www.foo.com you are already at https, that is the redirection has already happened, and there is no router rule to match.

1 Like

So, I'm afraid I'm misunderstanding the rule of HostRegexp or the role of wwwsecure-catchall.

The http -> https redirection is made at the entry point level, and it's great! So all routers have just to deal with https. In my initial idea, the router rule HostRegexp(`{host:(www\.).+}`) should catch urls like https://www.foo.com and then the router should pass them to the middleware wwwtohttps in order to remove the www and to pass forward the request https://foo.com. But this happens only if I specify the option wwwsecure-catchall.tls.

This solution didn't work for me. There are tons of answers but still there is no official document for this by Traefik.

I tried this on traefik.yml but didn't work too.

middlewares:
  nowww-redirectregex:
    redirectRegex:
      permanent: true
      regex: "^https://www.(.*)"
      replacement: "https://$1"