Middleware to add the "/" if needed

To complement defaultRule to get the service name without the stack
I am trying to make a common middleware that would strip the path prefix

        - traefik.http.middlewares.strip-prefix.stripprefixregex.regex=/[a-z0-9_]+

However, this causes an issue if I don't put the trailing slash. For example if I put in /whoami2 it keeps it at /whoami2, I want it to redirect to /whoami2/ This is because some apps like Portainer will not work if the path is /portainer but works with /portainer/

I would rather not put in a / at the end explicitly either.

I also tried (updated based on comment by @zespri

        - traefik.http.middlewares.strip-prefix.replacepathregex.regex=^/[a-z0-9_]+
        - traefik.http.middlewares.strip-prefix.replacepathregex.replacement=/

I also did the following which uses a redirectregex also no luck

    - traefik.http.middlewares.strip-prefix.chain.middlewares=strip-prefix-1,strip-prefix-2
    - traefik.http.middlewares.strip-prefix-1.redirectregex.regex=^(https?://[^/]+/[a-z0-9_]+)$$
    - traefik.http.middlewares.strip-prefix-1.redirectregex.replacement=$${1}/
    - traefik.http.middlewares.strip-prefix-1.redirectregex.permanent=true
    - traefik.http.middlewares.strip-prefix-2.stripprefixregex.regex=/[a-z0-9_]+

I think it's called ReplacePathRegex, not ReplacePath. Also if you use both stripprefixregex and replacepathregex you should give them different names using strip-prefix for both won't work...

Thanks for the catch, but that didn't work either :frowning:

Could you try something like that:

- traefik.http.middlewares.strip-prefix.replacepathregex.regex=^(/[a-z0-9_]+)
- traefik.http.middlewares.strip-prefix.replacepathregex.replacement=${1}/

https://docs.traefik.io/v2.0/middlewares/replacepathregex/

won't that preserve the prefix? I'm trying to remove the prefix that gets sent otherwise we'd have /portainer/portainer/

Here's the solution

    - traefik.http.middlewares.strip-prefix.chain.middlewares=strip-prefix-1,strip-prefix-2
    - traefik.http.middlewares.strip-prefix-1.redirectregex.regex=^(https?://[^/]+/[a-z0-9_]+)$$
    - traefik.http.middlewares.strip-prefix-1.redirectregex.replacement=$${1}/
    - traefik.http.middlewares.strip-prefix-1.redirectregex.permanent=true
    - traefik.http.middlewares.strip-prefix-2.stripprefixregex.regex=/[a-z0-9_]+
6 Likes

Now we just need to figure out ssl passthrough from that other thread :wink:

1 Like

I already solved that one in Trajano base Docker swarm stacks - Traefik v2 (latest) - Traefik Labs Community Forum

Oh right! I saw that but did not realize it was related.

@trajano why do you have both main and sans the same?

        - traefik.tcp.routers.intranet.tls.domains[0].main=i.trajano.net
        - traefik.tcp.routers.intranet.tls.domains[0].sans=i.trajano.net

@zespri wrong topic to ask that question.

That is the same as in the opening. I am also looking for solution for apps like portainer.

@trajano you freaking rock man!! You're my regex God :star_struck: :innocent: :smiling_face_with_three_hearts:

@pascalandy Also note that in yaml different escaping rules applies depending if you surround your string in quotation marks or not. In your original problem there were quotatation marks. So watch for these too

@trajano do you happen to know how to do the opposite? Meaning, to remove a slash if there is one

I'm running

version: "3.3"

services:

  hello:
    image: hashicorp/http-echo
    container_name: hello
    hostname: hello
    restart: unless-stopped
    command: "-listen=':5001' -text='Hello Pascal'"

    labels:
      #### core configs
      - "traefik.enable=true"
      - "traefik.http.routers.hello.rule=Host(`devkiwi.club`) && Path(`/hello`)"
      - "traefik.http.services.hello.loadbalancer.server.port=5001"
      #### set TLS (https)
      - "traefik.http.routers.hello.entrypoints=websecure"
      - "traefik.http.routers.hello.tls=true"
      - "traefik.http.routers.hello.tls.certresolver=leresolver"

      #### https://twitter.com/askpascalandy

Just reversed it here but I haven't tested it

basically the position of the / is swapped.

Thanks will test it!!

Thanks for the solution! I was going crazy trying to fix it.
I would be nice to have a better solution built in to Traefik to fix this kind of problem and make setup more similar to how Nginx behaves by default.

I had exact same issue: Cannot reacreate Nginx proxy_pass behavior with Traefik

1 Like

I had some issues with the other recommended solutions here. My goal was to get the /dashboard path to redirect to /dashboard/ so that the traefik dashboard would be easier to get to.

Here is the solution I ended up with... Note, it isn't a catchall and it is hard coded for the /dashboard path, but it is a good starting point that should be easy to expand with wildcards.

      labels:
        - traefik.enable=true

        # required in swarm, but can be any port number. 
        - traefik.http.services.api@internal.loadbalancer.server.port=8080
        - traefik.http.routers.dashboard.rule=Host(`example.com`) && (PathPrefix(`/dashboard`) || PathPrefix(`/api`))
        
        # middlewares
        - traefik.http.middlewares.dashboard-auth.basicauth.users={{ htpasswd }}
        - traefik.http.routers.dashboard.middlewares=dashboard-auth
        
        # Add trailing slash to /dashboard path
        - traefik.http.middlewares.dashboard-strip-slash.redirectregex.regex=(^.*\/dashboard$$)
        - traefik.http.middlewares.dashboard-strip-slash.redirectregex.replacement=$$1/
        - traefik.http.middlewares.dashboard-strip-slash.redirectregex.permanent=false
        - traefik.http.routers.dashboard.middlewares=dashboard-strip-slash