Nginx by default automatically adds HTTP redirect /foo -> /foo/ when using prefix match with trailing slash location /foo/ which is redirected to a backend (documented in the last paragraph here). This behavior is useful when serving a web application at http://example.com/foo/ and a user accesses http://example.com/foo. Unfortunately, this behavior is broken when using ingress.kubernetes.io/rewrite-target annotation, which turns location match into a non-prefix regex one.
I would argue that the behavior of adding a trailing slash by HTTP redirect on the base path of an web application exposed through an non-root Ingress (with or without a rewrite target) is very useful and should either work by default, or be enabled via some separate annotation.
I was unable to find a workaround in the latest version of Nginx ingress controller. Using /foo ingress path does not work as it breaks relative links inside the web application.
Also, if #619 would be fixed by unconditionally enabling regular expressions, even the currently working cases (without rewrite) will break.
Related: https://github.com/kubernetes/contrib/issues/1884
As a workaround ingress.kubernetes.io/configuration-snippet can be used (based on this comment by @hartmut-co-uk):
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: app
annotations:
ingress.kubernetes.io/rewrite-target: /
# adds 301 redirect with trailing slash
ingress.kubernetes.io/configuration-snippet: |
rewrite ^(/app)$ $1/ permanent;
spec:
rules:
- host: example.com
http:
paths:
- path: /app
backend:
serviceName: app
servicePort: 80
This works with ingress.kubernetes.io/rewrite-target: / probably by accident, the configuration snippet is being added just before other rewrite rules:
# Parts of generated /etc/nginx/nginx.conf
location ~* ^/app {
set $proxy_upstream_name "default-routes-app-80";
port_in_redirect off;
# enforce ssl on server side
if ($pass_access_scheme = http) {
return 301 https://$host$request_uri;
}
# ...
rewrite ^(/app)$ $1/ permanent;
rewrite /app/(.*) /$1 break;
rewrite /app / break;
proxy_pass http://default-routes-app-80;
}
Issues go stale after 90d of inactivity.
Mark the issue as fresh with /remove-lifecycle stale.
Stale issues rot after an additional 30d of inactivity and eventually close.
Prevent issues from auto-closing with an /lifecycle frozen comment.
If this issue is safe to close now please do so with /close.
Send feedback to sig-testing, kubernetes/test-infra and/or @fejta.
/lifecycle stale
Stale issues rot after 30d of inactivity.
Mark the issue as fresh with /remove-lifecycle rotten.
Rotten issues close after an additional 30d of inactivity.
If this issue is safe to close now please do so with /close.
Send feedback to sig-testing, kubernetes/test-infra and/or @fejta.
/lifecycle rotten
/remove-lifecycle stale
Rotten issues close after 30d of inactivity.
Reopen the issue with /reopen.
Mark the issue as fresh with /remove-lifecycle rotten.
Send feedback to sig-testing, kubernetes/test-infra and/or fejta.
/close
Further to what @rutsky mentioned in his comment if you are using nginx ingress controller specifically and your annotations start with nginx as follows:
metadata:
name: ingress-tutorial
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/rewrite-target: "/"
nginx.ingress.kubernetes.io/configuration-snippet: |
rewrite ^(/app)$ $1/ permanent;
Your annotation should start with nginx. and hence should read as nginx.ingress.kubernetes.io/configuration-snippet as opposed to ingress.kubernetes.io/configuration-snippet
Please keep in mind that while @ProProgrammer 's solution works 301 redirects are PERMANENT.
The unintended byproducts of forcing a trailing slash via 301 can be enough to get you fired.
It's likely safer to start with a 302 temporal redirect if you're just futzing around trying to get something operational.
Per the NGINX docs you can use the redirect param instead of the permanent param to provide a 302 instead of a 301 redirect.
name: ingress-tutorial
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/rewrite-target: "/"
nginx.ingress.kubernetes.io/configuration-snippet: |
rewrite ^(/app)$ $1/ redirect;
I can't quite get that rewrite rule/block to work. It redirects me to the (internal) load balancer IP.
Is there another config to explicitly set the host? nginx doesn't work if I specify the host (there's an internal load balancer), I think the DNS name is getting dropped from the public load balancer.
Most helpful comment
Related: https://github.com/kubernetes/contrib/issues/1884
As a workaround
ingress.kubernetes.io/configuration-snippetcan be used (based on this comment by @hartmut-co-uk):This works with
ingress.kubernetes.io/rewrite-target: /probably by accident, the configuration snippet is being added just before other rewrite rules: