Same host, one with PathPrefix, one without

Hi
I'm running traefik to help proxy stuff in our docker swarm.
Currently, we've only had 1 API, so it was nice and easy. Now, we have 2 different APIs we wish to run over the same host name.

So we have our original API, with a router rule of: Host(api.domain.tld) and our new API we wish to run like this: Host(api.domain.tld) && PathPrefix(/newstuff)

Is this approach the right? I cant seem to get it working. I have tried to attach "priority" as well, giving 1 to the original, and 2 to the new api.

Hi @jola :wave:
Welcome to the community.

Priority is based off rule length by default. Higher priority is evaluated first.

By default with no defined priority would be evaluated:
Host(`api.domain.tld`) && PathPrefix(`/newstuff`)
Host(`api.domain.tld`)

What in particular is not working?
Have you got accesslog enabled? Part of the output lists what router processed a request.
There may be other configuration issues(docker swarm, the common one is labels not nested under deploy), posting some config we can look for it.

Hi

Sorry for the long delay in reply. Project was holded a little, and i forgot to check up on it...

So, as mentioned, we have 2 apis, and swagger for both, and they both listen to internally /.

Re access log, can i get the output of that into the docker logs? Or does it have to be a file?

If I today go to api.domain.tld/swagger i get our current api
and my wish is, to have the new stuff on api.domain.tld/newstuff/swagger while having the new stuff listening on / internally.

The actual api reqeusts should also go to tne /newstuff so like GET api.domain.tld/newstuff/data internally in newstuff should be GET api.domain.tld/data but redirected to the new_api

This is the config setup I get from traefik

{
    "http": {
        "routers": {
            "current_api": {
                "entryPoints": ["web"],
                "service": "current_api",
                "rule": "Host(`api.domain.tld`)"
            },
            "new_api": {
                "entryPoints": ["web"],
                "service": "new_api",
                "rule": "Host(`api.domain.tld`) \\u0026\\u0026 Path(`/newstuff`)"
            },
            "frontend": {
                "entryPoints": ["web"],
                "service": "frontend",
                "rule": "Host(`domain.tld`)"
            }
        },
        "services": {
            "current_api": {
                "loadBalancer": {
                    "servers": [{
                            "url": "http://10.0.7.28:80"
                        }
                    ],
                    "passHostHeader": true
                }
            },
            "new_api": {
                "loadBalancer": {
                    "servers": [{
                            "url": "http://10.0.7.27:80"
                        }
                    ],
                    "passHostHeader": true
                }
            },
            "frontend": {
                "loadBalancer": {
                    "servers": [{
                            "url": "http://10.0.7.31:80"
                        }
                    ],
                    "passHostHeader": true
                }
            }
        },
    },
    "tcp": {},
    "udp": {}
}
" providerName = docker

I have also tried using the middleware stripprefix, which sets up like this:

{
    "http": {
        "routers": {
            "current_api": {
                "entryPoints": ["web"],
                "service": "current_api",
                "rule": "Host(`api.domain.tld`)"
            },
            "new_api": {
                "entryPoints": ["web"],
                "middlewares": ["new_api-replacepath"],
                "service": "new_api",
                "rule": "Host(`api.domain.tld`) \\u0026\\u0026 Path(`/newstuff`)"
            },
            "frontend": {
                "entryPoints": ["web"],
                "service": "frontend",
                "rule": "Host(`domain.tld`)"
            }
        },
        "services": {
            "current_api": {
                "loadBalancer": {
                    "servers": [{
                            "url": "http://10.0.7.28:80"
                        }
                    ],
                    "passHostHeader": true
                }
            },
            "new_api": {
                "loadBalancer": {
                    "servers": [{
                            "url": "http://10.0.7.27:80"
                        }
                    ],
                    "passHostHeader": true
                }
            },
            "frontend": {
                "loadBalancer": {
                    "servers": [{
                            "url": "http://10.0.7.31:80"
                        }
                    ],
                    "passHostHeader": true
                }
            }
        },
        "middlewares": {
            "new_api-replacepath": {
                "stripPrefix": {
                    "prefixes": ["/newstuff"],
                    "forceSlash": true
                }
            }
        }
    },
    "tcp": {},
    "udp": {}
}
" providerName = docker

Have you checked the access logs of traefik and your api servers. It looks like this should works and perhaps it is. If your api is returning the wrong paths in responses that could be what if wrong here .

Hi

So, after enabling the access log and tearing hair out over not seeing anything, I noticed the environment I was checking against was bypassing traefik and using a different reverse proxy.

So, this is an entry from the access log:

{
    "Method": "GET",
    "URL": {
        "Scheme": "",
        "Opaque": "",
        "User": null,
        "Host": "",
        "Path": "/newstuff/swagger/index.html",
        "RawPath": "",
        "ForceQuery": false,
        "RawQuery": "",
        "Fragment": ""
    },
    "Proto": "HTTP/1.1",
    "ProtoMajor": 1,
    "ProtoMinor": 1,
    "Header": {
        "Accept": ["text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"],
        "Accept-Encoding": ["gzip, deflate"],
        "Accept-Language": ["en-GB,en;q=0.9,da-DK;q=0.8,da;q=0.7,en-US;q=0.6"],
        "Cache-Control": ["max-age=0"],
        "Connection": ["keep-alive"],
        "Cookie": ["experimentation_subject_id=IjUxODY3MzFkLWU2ZmYtNDAzNy1iMTNkLTk5NDYxOTA5MDgzYyI%3D--856a269942d32c3c7ba51c15c5086a6c2774b066; sub=true; ajs_group_id=null; ajs_user_id=%22f4ea64d1051a627012a9d38f3a9c117aebbeeba1%22; ajs_anonymous_id=%223ff8d7d7-da1c-467a-aa47-734a27c6452a%22; __cfduid=dac37ecf2632783bee9934b39ef45160f1612774458"],
        "Upgrade-Insecure-Requests": ["1"],
        "User-Agent": ["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.146 Safari/537.36"],
        "X-Forwarded-Host": ["api.domain.tld"],
        "X-Forwarded-Port": ["80"],
        "X-Forwarded-Proto": ["http"],
        "X-Forwarded-Server": ["1ed50b3688c0"],
        "X-Real-Ip": ["10.255.0.2"]
    },
    "ContentLength": 0,
    "TransferEncoding": null,
    "Host": "api.domain.tld",
    "Form": null,
    "PostForm": null,
    "MultipartForm": null,
    "Trailer": null,
    "RemoteAddr": "10.255.0.2:54027",
    "RequestURI": "/newstuff/swagger/index.html",
    "TLS": null
}

and

10.255.0.2 - - [08/Feb/2021:09:31:36 +0000] "GET /newstuff/swagger/index.html HTTP/1.1" 404 0 "-" "-" 577 "current_api@docker" "http://10.0.7.28:80" 2ms

It seems the && PathPrefix doesn't get registered properly, since the GET request ends up at current_api

Okay, so I found the issue.

First, my config was using Path and not PathPrefix,
and secondly, I had the strip middleware disabled from my own testing.

Now, my only issue is mis-configurations on my own end.

Thanks for the help :smiley:

1 Like