Fighting trailing slash problem
During last few months i was part of team migrating “standard three tier” infrastructure to new docker and kubernetees infrastructure. We changed or added many crucial parts of our infrastructure including loadbalancers. In fact we added ingress controller/loadbalancer called traefik. Soon after deployment we realised we have small, but really annoying (especially for our customers) problem.
First i will try to explain how our https connection pipeline looks like in this picture.

As you can see client’s browser requests url https://server:443/app, but our nodejs app serves app as a / (root) without suffix. So we need to configure traefik to do 2 things
- route to correct container based on app suffix
- strip suffix when forwarding request to nodejs app container
First thing somehow comes out of box with correctly configured ingress in container definition.
For second we used ingress traefik annotation in our nodejs app pod:
ingress:
enabled: true
annotations:
"kubernetes.io/ingress.class": "traefik"
"traefik.frontend.rule.type": "PathPrefixStrip"
path: /app
This annotation removes /app (or any other suffix path defined in ingress path section). This annotation works, but with one small problem, it removes suffix when request is coming from traefik to nodejs pod, but doesn’t add it to response coming from nodejs pod.
So if app returns something like url for styles.css, it will be delivered to client browser like url to download https://server:443/styles.css NOT (as it should be) like https://server:443/app/styles.css
After few experiments I figured out that if you use in ingress something like (note i just added / to path)
ingress:
enabled: true
annotations:
"kubernetes.io/ingress.class": "traefik"
"traefik.frontend.rule.type": "PathPrefixStrip"
path: /app/everything works like expected. Response for browser is correct https://server:443/app/styles.css .
So problem seems solved, but new obstacle will rise. If your client will type into browser https://server:443/app , will get ugly 404 error. This is because path /app is not defined in ingress paths (there is /app/) .
Our first solution was to add some configuration to haproxy which lies between client browser and our traefik.
Approximately 2 weeks ago we decided to remove haproxy (better say to replace by AWS ALB) and to solve trailing slash problem within kuberneets or in traefik.
After few tries to convince traefik to work with my regular expresions to add / to end of url which is missing / (obvoiusly i don’t want to end with something like https://server:443/app/styles.css/ so regexp had to be a bit more complex).
Unfortunately it wasn’t way to go as trailing slash wasn’t added correctly. So I’ve started think about different solution.
Whole problem is that we need to add slash to end of specific url. We can do it with any webserver (like nginx) and send back redirect back to client. Client then will connect to correct url ending with slash.
We just need now to get all requests which ends with 404 (because ingress path of our app is ending with / and but browser url isn’t) to some pod with nginx configured to do redirect. Of course we need to keep in mind we cannot do it for ingress paths used by other pods.
For this we will use nginx pod with following annotation in ingress
ingress:
enabled: true
annotations:
"kubernetes.io/ingress.class": "traefik"
"traefik.frontend.rule.type": "PathPrefix"
"traefik.frontend.redirect.entryPoint": "https"
"traefik.ingress.kubernetes.io/priority": "1"
path: /This will add to ingress controller (traefik) following rule: With lowest priority match path / for any servername.
It acts like: match this if nothing else can be matched (this ensures bold line with priority setting).
Now we just need pod with some nginx and redirect configuration. Something like this.
server {
listen 8080 default_server;
server_name _;
server_tokens off;
location / {
rewrite ^/([^/.]+)$ https://$host/$1/ redirect;
}
}This will redirect only if / is not already presented in path. Reason for this is that this catch all pod can be used for more purposes. You can for example redirect /whatever (of course if is not url of app) to valid app url (what we really do).
server {
listen 8080 default_server;
server_name _;
server_tokens off;
location / {
rewrite ^/([^/.]+)$ https://$host/$1/ redirect;
return 301 https://$host/my-default-app;
}
}How it works ?
1. https://server:443/whatever is redirected to https://server:443/whatever/
2. https://server:443/whatever/ doesn’t match redirection rule in nginx (as it has / at the end) and bold line rule is applied to it.
My sample helm chart for this root-proxy pod can be found here.
