Redirect HTTP to HTTPS and WWW to Non-WWW with Traefik 3
Tags: tutorial, docker, traefik, linux
Published: 2023-05-07 10:04:18

## Introduction
[Traefik](https://doc.traefik.io/traefik/) is awesome! If you, like me, have moved all your web services to Docker and Docker Compose, there is no better option for a reverse proxy and load balancer than Traefik in my opinion.

You can get things done quickly without having to write massive config files.

However, when you're just starting out with Traefik it might take some getting used to.

Looking at some useful, real-world examples might help in that situation!

## Redirecting HTTP to HTTPS
One of the most common things you would want your reverse proxy to handle is automatically redirecting HTTP traffic to HTTPS.

Example: http://fabiancdng.com should automatically be redirected to https://fabiancdng.com to establish a secure connection.

### The example web service
To show you how we could go about automatically redirecting HTTP to HTTPS in Traefik, it makes sense to look at an example Docker Compose stack that is already set up to run behind Traefik as a reverse proxy.

```yaml
version: '3.7'

services:
  fabiancdng-website:
    image: registry/.../my-website-image:latest
    labels:
      - traefik.enable=true
      - traefik.http.routers.my-website-frontend-http.rule=Host(`fabiancdng.com`)
      - traefik.http.routers.my-website-frontend-http.entrypoints=http
      - traefik.http.services.my-website-frontend.loadbalancer.server.port=3000
    networks:
      - proxy

networks:
  proxy:
    external: true
```

Traefik and this example service can communicate through the external Docker network "proxy". This can differ in your case based on your own configuration.

The above example creates an HTTP [router](https://doc.traefik.io/traefik/routing/routers/) `my-website-frontend-http` that accepts incoming traffic to http://fabiancdng.com and routes it to port 3000 of the underlying container (that port is not exposed publicly though; only in the Docker network).

### Routing HTTPS traffic to the same container
Let's extend the `docker-compose.yml` to also accept HTTPS traffic at https://fabiancdng.com:

```yaml
version: '3.7'

services:
  fabiancdng-website:
    image: registry/.../my-website-image:latest
    labels:
      - traefik.enable=true
      - traefik.http.routers.my-website-frontend-http.rule=Host(`fabiancdng.com`)
      - traefik.http.routers.my-website-frontend-http.entrypoints=http
      - traefik.http.routers.my-website-frontend-https.rule=Host(`fabiancdng.com`)
      - traefik.http.routers.my-website-frontend-https.entrypoints=https
      - traefik.http.routers.my-website-frontend-https.tls=true
      - traefik.http.routers.my-website-frontend-https.tls.certresolver=letsencrypt
      - traefik.http.services.my-website-frontend.loadbalancer.server.port=3000
    networks:
      - proxy

networks:
  proxy:
    external: true
```

In this example configuration, we simply added the router `my-website-frontend-https` that accepts HTTPS traffic on https://fabiancdng.com.

Additionally, we introduced a [certificate resolver](https://doc.traefik.io/traefik/https/acme/#certificate-resolvers) `letsencrypt`.

**The certificate resolver is optional but I highly recommend using an SSL certificate on HTTPS routes.**

### The traefik.yml configuration file
For all of this to work it's important that you configured the basics in Traefik correctly. There need to be corresponding entry points for `http` and `https` on the correct ports.

This is how my traefik.yml configuration file handles those:

```yaml
entryPoints:
  http:
    address: ':80'
  https:
    address: ':443'

providers:
  docker:
    network: proxy

certificatesResolvers:
  letsencrypt:
    acme:
      email: ...
      storage: acme.json
      httpChallenge:
        entryPoint: http
```

Again, the certificate resolver is optional but if you want to use it, you need a volume to store the `acme.json` for the certificates persistently. Also, make sure to adjust the Docker network to your existing configuration.

### Finally: The redirect from HTTP to HTTPS
Now that we accept incoming traffic both at http://fabiancdng.com and https://fabiancdng.com, we can simply redirect the traffic hitting the `my-website-frontend-http` router to the `my-website-frontend-https` router using a [RedirectScheme middleware](https://doc.traefik.io/traefik/middlewares/http/redirectscheme/):

```yaml
version: '3.7'

services:
  fabiancdng-website:
    image: registry/.../my-website-image:latest
    labels:
      - traefik.enable=true
      - traefik.http.routers.my-website-frontend-http.rule=Host(`fabiancdng.com`)
      - traefik.http.routers.my-website-frontend-http.entrypoints=http
      - traefik.http.routers.my-website-frontend-http.middlewares=redirect-to-https
      - traefik.http.routers.my-website-frontend-https.rule=Host(`fabiancdng.com`)
      - traefik.http.routers.my-website-frontend-https.entrypoints=https
      - traefik.http.routers.my-website-frontend-https.tls=true
      - traefik.http.routers.my-website-frontend-https.tls.certresolver=letsencrypt
      - traefik.http.services.my-website-frontend.loadbalancer.server.port=3000
      - traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https
      - traefik.http.middlewares.redirect-to-https.redirectscheme.permanent=true
    networks:
      - proxy

networks:
  proxy:
    external: true
```

As you can see in this example, we created a middleware for the `http` entrypoint that redirects with a 301 to HTTPS:

```
- traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https
- traefik.http.middlewares.redirect-to-https.redirectscheme.permanent=true
```

And we registered the middleware to the `my-website-frontend-http` router:

```
- traefik.http.routers.my-website-frontend-http.middlewares=redirect-to-https
```

Great! That should do it for the HTTPS redirect.

## Redirecting WWW to Non-WWW
One thing that might be important as well when deploying a public website is to redirect traffic hitting the `www` subdomain to the non-www domain (or vice-versa).

When both your main domain and the www subdomain (or even all subdomains) point to your server running Traefik, it makes sense to redirect the traffic to your preferred version.

### Why even redirect?
Even though you could use [canonicalization](https://fabiancdng.com/blog/seo-for-developers-pagination) to prevent duplicate content SEO issues, it's better to not have duplicate content in the first place and just redirect to your preferred URL for a piece of content.

### Regex middleware to redirect www to non-www
As you might have figured, we need another middleware to pull off this redirect as well.

Simply add the following labels to your configuration:

```
- traefik.http.middlewares.redirect-to-non-www.redirectregex.regex=^https?://www.fabiancdng.com/(.*)
- traefik.http.middlewares.redirect-to-non-www.redirectregex.replacement=https://fabiancdng.com/$${1}
- traefik.http.middlewares.redirect-to-non-www.redirectregex.permanent=true
```

Also, you need to accept the www subdomain in your router as well. You can simply use `||` and add another Host to both the `my-website-frontend-http` and `my-website-frontend-https` router:

```
- traefik.http.routers.my-website-frontend-http.rule=Host(`fabiancdng.com`) || Host(`www.fabiancdng.com`)
- traefik.http.routers.my-website-frontend-https.rule=Host(`fabiancdng.com`) || Host(`www.fabiancdng.com`)
```

**Make sure to change the domain to your domain.**

The last step now is to hook up the new middleware to your `my-website-frontend-https` router (that one router should be sufficient as all traffic gets redirected there anyway):

```
- traefik.http.routers.my-website-frontend-https.middlewares=redirect-to-non-www
```

### Everything put together
The whole configuration should look something like this now:

```yaml
version: '3.7'

services:
  fabiancdng-website:
    image: registry/.../my-website-image:latest
    labels:
      - traefik.enable=true
      - traefik.http.routers.my-website-frontend-http.rule=Host(`fabiancdng.com`) || Host(`www.fabiancdng.com`)
      - traefik.http.routers.my-website-frontend-http.entrypoints=http
      - traefik.http.routers.my-website-frontend-http.middlewares=redirect-to-https
      - traefik.http.routers.my-website-frontend-https.rule=Host(`fabiancdng.com`) || Host(`www.fabiancdng.com`)
      - traefik.http.routers.my-website-frontend-https.entrypoints=https
      - traefik.http.routers.my-website-frontend-https.tls=true
      - traefik.http.routers.my-website-frontend-https.tls.certresolver=letsencrypt
      - traefik.http.routers.my-website-frontend-https.middlewares=redirect-to-non-www
      - traefik.http.services.my-website-frontend.loadbalancer.server.port=3000
      - traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https
      - traefik.http.middlewares.redirect-to-https.redirectscheme.permanent=true
      - traefik.http.middlewares.redirect-to-non-www.redirectregex.regex=^https?://www.fabiancdng.com/(.*)
      - traefik.http.middlewares.redirect-to-non-www.redirectregex.replacement=https://fabiancdng.com/$${1}
      - traefik.http.middlewares.redirect-to-non-www.redirectregex.permanent=true
    networks:
      - proxy

networks:
  proxy:
    external: true
```

Done, both redirects should be working now!

## Conclusion and further resources
Once you've gotten used to the way you write configuration and middlewares using the labels in Traefik, it's not that complicated after all (well- except when you need regex 😅; but you can just google that).

And being able to handle all these things properly in your reverse proxy can massively improve the user experience and SEO of your web services.

Lastly, if you want to learn even more about Traefik and how to work with it in more complex scenarios, I can recommend deploying a more complex service like, for instance, Nextcloud (even if it's just for practicing).

A while back, I wrote a blog post going into detail on how Nextcloud can be deployed with Docker and run behind Traefik as a reverse proxy.

Feel free to check that out: https://fabiancdng.com/blog/running-nextcloud-using-docker-and-traefik-3

I even wrote an article on how Traefik can work as a load balancer for Docker containers: https://fabiancdng.com/blog/scaling-next-js-web-apps-with-docker

Traefik is an awesome reverse proxy and I hope this example helped you gain more inside in Traefik's configuration system.

Cheers!