How to redirect main domain to external

Dear All,
I'm new to Traefik, and have done a lot of reading before attempting to install it for my VPS. Now I am at a stage where all the services that I need are exposed. However, I cannot get the main domain to redirect (anywhere). I would like to redirect it to another site.

In the DEBUG log I can see, that all the subdomains are getting SSL certificates (if they need), but if I visit mydomain.com or www.mydomain.com I get a similar error:

time="2023-07-18T17:58:29+03:00" level=debug msg="Serving default certificate for request: \"www.mydomain.com\""
time="2023-07-18T17:58:29+03:00" level=debug msg="http: TLS handshake error from 172.68.62.27:56516: remote error: tls: unknown certificate authority"

My server is behind Cloudflare, but I don't think that is an issue as all the subdomains work correctly.

This is my docker-compose.yml:

version: "3"

services:
  traefik:
    image: traefik:latest
    container_name: traefik
    restart: unless-stopped
    networks:
      - mynet
    ports:
      - 80:80
      - 443:443
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /etc/localtime:/etc/localtime:ro
      - ./data:/etc/traefik
      - ./ssl-certs:/ssl-certs
      - ./logs:/var/log/traefik

    labels:
      - traefik.enable=true

      - traefik.http.routers.traefik.entrypoints=websecure
      - traefik.http.routers.traefik.rule=Host(`traefik.mydomain.com`)
      - traefik.http.routers.traefik.tls=true
      - traefik.http.routers.traefik.tls.certresolver=production
      - traefik.http.services.traefik.loadbalancer.server.port=8080

      - traefik.http.routers.catchall.entrypoints=websecure
      - traefik.http.routers.catchall.rule=HostRegexp(`{host:.+}`)
      - traefik.http.routers.catchall.priority=1
      - traefik.http.routers.catchall.tls.certresolver=myresolver
      - traefik.http.services.catchall.loadbalancer.server.port=80
      - traefik.http.routers.catchall.middlewares=redirectall
      - traefik.http.middlewares.redirectall.redirectregex.regex=.*
      - traefik.http.middlewares.redirectall.redirectregex.replacement=https://anotherdomain.com

networks:
  mynet:
    external: true

Finally, here is my traefik.yml:

global:
  checkNewVersion: true

log:
  level: DEBUG 
  format: common  
  filePath: /var/log/traefik/traefik.log

api:
  dashboard: false 
  insecure: false

entryPoints:
  web:
    address: :80
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https

  websecure:
    address: :443

certificatesResolvers:
  staging:
    acme:
      email: letsencrypt@mydomain.com
      storage: /etc/traefik/certs/acme.json
      caServer: "https://acme-staging-v02.api.letsencrypt.org/directory"
      httpChallenge:
        entryPoint: web

  production:
    acme:
      email: letsencrypt@mydomain.com
      storage: /etc/traefik/certs/acme.json
      caServer: "https://acme-v02.api.letsencrypt.org/directory"
      httpChallenge:
        entryPoint: web

providers:
  docker:
    exposedByDefault: false
  file:
    directory: /etc/traefik/configuration
    watch: true

What am I missing?

See simple Traefik example, redirect at the bottom.

You should first check that your router works, maybe with a whoami target service, then add the middleware. You could use HostSNI(`*`) for websecure catchall rule.

I'm afraid I don't quite understand what you mean. I had a look at the example you have shown (in fact, I had been experimenting with it before), and while it works as it is (well, I added tls to it), it didn't get me any closer to the solution.

Entering whoami.mydomain.com works fine, but www.whoami.mydomain.com fails with the following error in the browser (Brave, incognito mode):

This site can’t provide a secure connection www.whoami.mydomain.com uses an unsupported protocol.
ERR_SSL_VERSION_OR_CIPHER_MISMATCH

Also, what do you mean by:

You could use HostSNI(*) for websecure catchall rule.

I like the idea of a catchall rule... but where do I put it?

<thanks for sticking with me on this issue!>

I managed to redirect the main domain like this:

      # Redirecting main domain
      - "traefik.http.routers.domain.entrypoints=websecure"
      - "traefik.http.routers.domain.rule=Host(`mydomain.com`)"
      - "traefik.http.routers.domain.tls.certresolver=production"
      - "traefik.http.routers.domain.middlewares=domain"
      - 'traefik.http.middlewares.domain.redirectregex.regex=^https://mydomain.com/(.*)'
      - 'traefik.http.middlewares.domain.redirectregex.replacement=https://google.com/$${1}'
      - "traefik.http.middlewares.domain.redirectregex.permanent=false"

This is now working as expected. Is this the preferred way to do it, though?
Also, this only redirects the main domain, but not all the "non-existing" subdomains.

Traefik uses a router to match a route, then applies middleware, to finally forward via service.

traefik.http.routers.domain.rule=HostSNI(`*`) is a catchall for https/TLS connections.

This requires that you use a wildcard cert, so that Traefik knows what domains to get a cert for, and also that browsers will not show "invalid cert".

And you need to update your current RegEx to match any domain.

I'm happy to use a wildcard certificate, but where do I set it? And how do I make sure that this catchall router only forwards the "unknown" subdomains?

If I replace my - "traefik.http.routers.domain.rule=Host(mydomain.com)" line with your suggestion traefik.http.routers.domain.rule=HostSNI(*) then the main domain redirection stops working with 404 page not found and all unknown subdomains fail with Invalid SSL certificate.

As I stated before

This requires that you use a wildcard cert, so that Traefik knows what domains to get a cert for, and also that browsers will not show "invalid cert".

Either you specify all used domains with Host() || Host() || ... or you use a wildcard cert, either by buying one or using LetsEncrypt with dnsChallnge (doc).

I've been trying to get this working all day long, with no luck.
I have now this in my traefik.yml:

certificatesResolvers:
  production:
    acme:
      email: letsencrypt@mydomain.com
      storage: /etc/traefik/certs/acme.json
      caServer: "https://acme-v02.api.letsencrypt.org/directory"
      dnsChallenge:
        provider: cloudflare
        delayBeforeCheck: 0
        resolvers:
          - "1.1.1.1:53"
          - "8.8.8.8:53"

and this in docker-compose.yml:

      - traefik.http.routers.http_catchall.rule=HostRegexp(`{any:.+}`)
      - traefik.http.routers.http_catchall.entrypoints=web,websecure
      - traefik.http.routers.http_catchall.middlewares=default_host      

      - traefik.http.middlewares.default_host.redirectregex.regex=^https://(.*)?mydomain.com(.*)$$
      - traefik.http.middlewares.default_host.redirectregex.replacement=https://google.com
      - traefik.http.middlewares.default_host.redirectregex.permanent=false
      
      - traefik.http.routers.wildcard.tls=true
      - traefik.http.routers.wildcard.tls.certresolver=production
      - traefik.http.routers.wildcard.tls.domains[0].main=mydomain.com
      - traefik.http.routers.wildcard.tls.domains[0].sans=*.mydomain.com      

Still, no luck. All the subdomains defined work well, the main domain redirects correctly as well. But an undefined subdomain, like blah.mydomain.com gives me a 404 page not found error.

In the logs I can't see any error:

time="2023-07-19T18:04:20+03:00" level=info msg="Traefik version 2.10.3 built on 2023-06-19T16:18:54Z"
time="2023-07-19T18:04:20+03:00" level=info msg="Starting provider aggregator aggregator.ProviderAggregator"
time="2023-07-19T18:04:20+03:00" level=info msg="Starting provider *file.Provider"
time="2023-07-19T18:04:20+03:00" level=info msg="Starting provider *traefik.Provider"
time="2023-07-19T18:04:20+03:00" level=info msg="Starting provider *docker.Provider"
time="2023-07-19T18:04:20+03:00" level=info msg="Starting provider *acme.ChallengeTLSALPN"
time="2023-07-19T18:04:20+03:00" level=info msg="Starting provider *acme.Provider"
time="2023-07-19T18:04:20+03:00" level=info msg="Testing certificate renew..." providerName=production.acme ACME CA="https://acme-v02.api.letsencrypt.org/directory"
time="2023-07-19T18:04:20+03:00" level=info msg="Starting provider *acme.Provider"
time="2023-07-19T18:04:20+03:00" level=info msg="Testing certificate renew..." providerName=staging.acme ACME CA="https://acme-staging-v02.api.letsencrypt.org/directory"

Routers need a rule to be matched, routers.wildcard probably does nothing.

Assign your certresolver to websecure entrypoint.

See simple Traefik example.

ok, I think I understand this, and it makes sense:

So I changed my config like this, I even put in priority:

      - traefik.http.routers.http_catchall.priority=999999
      - traefik.http.routers.http_catchall.rule=HostRegexp(`{any:.+}`)
      - traefik.http.routers.http_catchall.entrypoints=websecure
      - traefik.http.routers.http_catchall.middlewares=default_host      

      - traefik.http.middlewares.default_host.redirectregex.regex=^https://(.*)?mydomain.com(.*)$$
      - traefik.http.middlewares.default_host.redirectregex.replacement=https://google.com
      - traefik.http.middlewares.default_host.redirectregex.permanent=false
      
      - traefik.http.routers.http_catchall.tls=true
      - traefik.http.routers.http_catchall.tls.certresolver=production
      - traefik.http.routers.http_catchall.tls.domains[0].main=mydomain.com
      - traefik.http.routers.http_catchall.tls.domains[0].sans=*.mydomain.com      

Now I am getting Too many redirects in the browser, for any request.

That does not make sense, when you redirect the browser to an external domain, like you show in your config. This usually just happens when you redirect to the same server and enter an endless loop.

After a lot more research, I am finally starting to begin to understand how Traefik works. :slight_smile:

I managed to get the desired behavior with the following setup:

I removed all shenanigans from docker-compose.yml, and added the following to the dynamic config:

  routers:
    domainRouter:
      entrypoints:
        - websecure
      rule: "Host(`mydomain.com`)"
      service: dummy
      middlewares:
        - domain
      tls:
        certresolver: production

      
    catchAllRouter:
      entrypoints:
        - websecure
      middlewares:
        - catchAll
      rule: "PathPrefix(`/`)"
      service: dummy
      priority: 1
      tls:
        certresolver: production
        domains:
          - main: "mydomain.com"
          - sans: "*.mydomain.com"

  middlewares:
    domain:
      redirectregex:
        regex: "^https://mydomain.com/(.*)"
        replacement: "https://mydomain.com/${1}"
        permanent: true

    catchAll:
      redirectregex:
        regex: "^.*$"
        replacement: "https://mydomain.com/${1}"
        permanent: false

This accomplishes the following:

  1. The main domain is redirected to a custom URL
  2. Any subdomain that is not explicitly defined in docker label rules get redirected to another custom domain.

@bluepuma77 thank you for nudging me in the right direction!

1 Like

This topic was automatically closed 3 days after the last reply. New replies are no longer allowed.