Skip to content

Instantly share code, notes, and snippets.

@kekru
Last active March 12, 2024 15:15
Show Gist options
  • Save kekru/d088be6a3fa844089ae62d80c077bb38 to your computer and use it in GitHub Desktop.
Save kekru/d088be6a3fa844089ae62d80c077bb38 to your computer and use it in GitHub Desktop.
Traefik redirect / (root) to sub path with Docker labels

Traefik: redirect base or root path to a subpath

This is tested with Traefik 1.7

This is how to redirect the root or base path to a sub path in Traefik using Docker labels:
Goals

  • https://example.com -> https://example.com/abc/xyz/
  • https://example.com/ -> https://example.com/abc/xyz/
  • https://example.com/something -> no redirect

We will match <begin of line>https://<any chars but not slash AS group1><slash or nothing><end of line>
and replace it with https://<group1>/abc/xyz/.
In regex we have to escape a / character by \/. In docker-compose labels we need to escape again, so that it becomes \\\\/.
We also need to escape $ to $$ because of docker-compose.

labels:
  - "traefik.frontend.rule=Host:example.com"
  - "traefik.frontend.redirect.regex=^https:\\\\/\\\\/([^\\\\/]+)\\\\/?$$"
  - "traefik.frontend.redirect.replacement=https://$$1/abc/xyz/"
  - "traefik.port=80"
  - "traefik.enable=true"
@flippy1345
Copy link

I leave this her just in case someone like me is looking for a way to have this in v2 and Kubernetes

apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: traefik-dashboard-redirect
  namespace: default
spec:
  redirectRegex:
    regex: ^https:\/\/([^\/]+)\/?$
    replacement: https://${1}/dashboard/

This would allow access to the traefik dashboard via "https://traefik.foo.bar" The URL would change like this: "https://traefik.foo.bar" -> "https//traefik.foo.bar/dashboard/"

Thanks for the effort, but this just ends up with an infinite loop.

It can be because I redirect hard to https.
This is my complete configuration with https redirect, regex path redirect and ip whitelist:

-- Ingress --
traefik-dashboard

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
    traefik.ingress.kubernetes.io/router.middlewares: default-traefik-dashboard-chain@kubernetescrd
  name: traefik-dashboard
  namespace: kube-system
spec:
  rules:
  - host: traefik.DOMAIN.de
    http:
      paths:
      - backend:
          service:
            name: traefik-dashboard
            port:
              number: 9000
        pathType: ImplementationSpecific
  tls:
  - hosts:
    - traefik.DOMAIN.de
    secretName: traefik-DOMAIN-de-tls

-- Traefik Middleware --
traefik-dashboard-chain

apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: traefik-dashboard-chain
  namespace: default
spec:
  chain:
    middlewares:
    - name: internal-dashboard-chain
    - name: traefik-dashboard-redirect

internal-dashboard-chain

apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: internal-dashboard-chain
  namespace: default
spec:
  chain:
    middlewares:
    - name: redirect-https
    - name: ip-whitelist

redirect-https

apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: redirect-https
  namespace: default
spec:
  redirectScheme:
    permanent: true
    scheme: https

ip-whitelist

apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: ip-whitelist
  namespace: default
spec:
  ipWhiteList:
    sourceRange:
    - 49.XXX.XXX.XXX #My Home IP

traefik-dashboard-redirect

apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: traefik-dashboard-redirect
  namespace: default
spec:
  redirectRegex:
    regex: ^https:\/\/([^\/]+)\/?$
    replacement: https://${1}/dashboard/

@idhamari
Copy link

idhamari commented Mar 23, 2023

Thanks, everyone for your contributions. I just started trying Traefik today and unfortunately, the above solutions do not work for me. Here is my setup:

two very simple static webapps, each app running in a separate container

        app1/
                index.html
                subfolder/index.html
        app2/
                index.html
                subfolder/index.html

Here are the contents of the html files:

    <!DOCTYPE html>
    <html>
    <head>
    <title>app1</title>
    </head>
    <body>
    <h1>App1 website home </h1>
    <p>Test reverse proxy !</p>
    <p><a href="subfolder/index.html">App1 subfolder</a></p>
    </body>
    </html>

    <!DOCTYPE html>
    <html>
    <head>
    <title>app1 subfolder</title>
    </head>
    <body>
    <h1>App1 website subfolder </h1>
    <p>Test reverse proxy subfolder webpage!</p>
    <p><a href="../index.html">App1 Home</a></p>
    </body>
    </html>

I am trying to access app1 and app2 via something like

        https://myexample.com/app1
        https://myexample.com/app2

With the setup below, I can access the main page of each app but not the subfolders.

        version: "3.3"

        services:

          traefik:
            image: "traefik:latest"
            container_name: traefik
            command:
              - "--log.level=DEBUG"
              - "--api.insecure=true"
              - "--providers.docker=true"
              - "--providers.docker.exposedbydefault=false"
              - "--entrypoints.web.address=:80"
            ports:
              - "80:80"
              - "8080:8080"
            volumes:
              - "/var/run/docker.sock:/var/run/docker.sock:ro"

          app1:
            image: "nginxdemos/hello"
            container_name: app1
            scale: 1
            volumes:
                - ./vols/app1:/usr/share/nginx/html

            labels:
              - "traefik.enable=true"
              - "traefik.http.routers.app1.entrypoints=web"
              - "traefik.http.routers.app1.rule=Host(`myexample.com`) && Path(`/app1`,`/app1/`)"
              - "traefik.http.routers.app1.middlewares=add-context"
              - "traefik.http.middlewares.add-context.redirectregex.regex=^https:\\/\\/([^\\/]+)\\/?$$"
              - "traefik.http.middlewares.add-context.redirectregex.replacement=https://$$1/app1/"
          app2:
            image: "nginxdemos/hello"
            container_name: app2
            scale: 1
            volumes:
                - ./vols/app2:/usr/share/nginx/html
            labels:
              - "traefik.enable=true"
              - "traefik.http.routers.app2.entrypoints=web"                  
              - "traefik.http.routers.app2.rule=Host(`myexample.com`) && Path(`/app2/`)"
              - "traefik.http.routers.app2.middlewares=add-context"
              - "traefik.http.middlewares.add-context.redirectregex.regex=^https:\\/\\/([^\\/]+)\\/?$$"
              - "traefik.http.middlewares.add-context.redirectregex.replacement=https://$$1/app2/"

The routing seems to work, e.g. I can see in the browser address bar:

          https://myexample.com/app1/subfolder/index.html 

But the page is not displayed (seems the path is not correct) and I get the 404 page not found error.

My network setup as follows:

  • Mainserver connected to a domain myexample.com
  • the Mainserver has nginx that sends incoming requests to the traefik machine 80 port using reverse proxy.

What I am missing?

@flippy1345
Copy link

flippy1345 commented Mar 25, 2023

@idhamari ,
my first guess would be a trailing / to much or to few

Also try to use the following:

  - "traefik.http.middlewares.add-context.redirectregex.regex=^https:\/\/([^\/]+)\/?$"
  - "traefik.http.middlewares.add-context.redirectregex.replacement=https://${1}/app1/"

  - "traefik.http.middlewares.add-context.redirectregex.regex=^https:\/\/([^\/]+)\/?$"
  - "traefik.http.middlewares.add-context.redirectregex.replacement=https://${1}/app2/"

@idhamari
Copy link

@flippy1345 thanks for your reply. Still nothing works.

@yunkuangao
Copy link

Thank you sincerely.

@kekru
Copy link
Author

kekru commented Apr 9, 2023

@idhamari sorry for late answer, but for me this is working:

services:

  traefik:
    image: "traefik:2.9.10"
    container_name: traefik
    command:
      - "--log.level=DEBUG"
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
    ports:
      - "80:80"
      - "8080:8080"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"

  app1:
    image: traefik/whoami:v1.8.7
    container_name: app1
    scale: 1
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.app1.entrypoints=web"
      - "traefik.http.routers.app1.rule=PathPrefix(`/app1`)"
      - "traefik.http.routers.app1.middlewares=add-context1"
      - "traefik.http.middlewares.add-context1.redirectregex.regex=^(.+/app1)$$"
      - "traefik.http.middlewares.add-context1.redirectregex.replacement=$$1/"
  app2:
    image: traefik/whoami:v1.8.7
    container_name: app2
    scale: 1
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.app2.entrypoints=web"                  
      - "traefik.http.routers.app2.rule=PathPrefix(`/app2`)"
      - "traefik.http.routers.app2.middlewares=add-context2"
      - "traefik.http.middlewares.add-context2.redirectregex.regex=^(.+/app2)$$"
      - "traefik.http.middlewares.add-context2.redirectregex.replacement=$$1/"

Changes that I made:

  • removed Host(myexample.com), because you only have one host
  • changed add-context to add-context1 and add-context2 -> you need unique names and not reuse it between services
  • use PathPrefix instead of Path, so we can match /app1 and /app1/
  • easier regex: ^(.+/app1)$$ will match anything with ends with /app1
  • use image traefik/whoami:v1.8.7 instead of nginx only for easier testing

Try also to look at servername:8080 to see the traefik dashboard. There I could see that there was a problem using add-context in both services.
To evaluate regexes you can use https://regexr.com/

@y0nei
Copy link

y0nei commented Oct 7, 2023

anyone got it working for v3? It does an infinite redirect loop

@N-B-H
Copy link

N-B-H commented Mar 10, 2024

anyone got it working for v3? It does an infinite redirect loop

I had the same issue and spent pretty much a whole day to figure it out.
Simple redirect from my root directory to /de (or other subfolder whatsoever)

Valid for my-domain.com and www.my-domain.com, http and https.

For me, in my environment, self-hosted on coolify, this worked:


traefik.http.middlewares.redirect-to-de.redirectregex.regex=^(https?://(?:www\.)?my-domain\.com)/?$$
traefik.http.middlewares.redirect-to-de.redirectregex.replacement=$${1}/de

and the redirect-to-de middleware has to be added to each router. The full config is in my case:

traefik.enable=true
traefik.http.middlewares.gzip.compress=true
traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https
traefik.http.middlewares.redirect-to-de.redirectregex.regex=^(https?://(?:www\.)?my-domain\.com)/?$$
traefik.http.middlewares.redirect-to-de.redirectregex.replacement=$${1}/de
traefik.http.middlewares.redirect-to-de.redirectregex.permanent=true
traefik.http.routers.http-0-mycontainer.entryPoints=http
traefik.http.routers.http-0-mycontainer.middlewares=redirect-to-https,redirect-to-de
traefik.http.routers.http-0-mycontainer.rule=Host(`my-domain.com`) && PathPrefix(`/`)
traefik.http.routers.http-0-mycontainer.service=http-0-mycontainer
traefik.http.routers.http-1-mycontainer.entryPoints=http
traefik.http.routers.http-1-mycontainer.middlewares=redirect-to-https,redirect-to-de
traefik.http.routers.http-1-mycontainer.rule=Host(`www.my-domain.com`) && PathPrefix(`/`)
traefik.http.routers.http-1-mycontainer.service=http-1-mycontainer
traefik.http.routers.https-0-mycontainer.entryPoints=https
traefik.http.routers.https-0-mycontainer.middlewares=gzip,redirect-to-de
traefik.http.routers.https-0-mycontainer.rule=Host(`my-domain.com`) && PathPrefix(`/`)
traefik.http.routers.https-0-mycontainer.service=https-0-mycontainer
traefik.http.routers.https-0-mycontainer.tls.certresolver=letsencrypt
traefik.http.routers.https-0-mycontainer.tls=true
traefik.http.routers.https-1-mycontainer.entryPoints=https
traefik.http.routers.https-1-mycontainer.middlewares=gzip,redirect-to-de
traefik.http.routers.https-1-mycontainer.rule=Host(`www.my-domain.com`) && PathPrefix(`/`)
traefik.http.routers.https-1-mycontainer.service=https-1-mycontainer
traefik.http.routers.https-1-mycontainer.tls.certresolver=letsencrypt
traefik.http.routers.https-1-mycontainer.tls=true
traefik.http.services.http-0-mycontainer.loadbalancer.server.port=80
traefik.http.services.http-1-mycontainer.loadbalancer.server.port=80
traefik.http.services.https-0-mycontainer.loadbalancer.server.port=80
traefik.http.services.https-1-mycontainer.loadbalancer.server.port=80


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment