When migrating from one domain name to another one may need to set up a redirect of types 301 (Moved Permanently) or 302 (Found) for a certain domain and its paths. If you have Kubernetes as your runtime, there are at least two ways to do that:
Deploy nginx server as an additional service to handle redirects.
Handle redirects at your ingress controller.
The first one is safer but a bit more complex, the second one utilizes the ingress controller of your cluster, so you get redirects "for free", but it comes with a penalty. Let's look at both of these ways in detail.
As an example, we will set up redirects from old-domain.tld
to new-domain.tld
for two paths:
/pages/about
→/about
with301 Moved Permanently
status/
→/
with302 Found
status
Additional service to handle redirects
Not to create all k8s resources from scratch one can use Bitmani's nginx helm chart.
Here is an example of values.yaml
file that configures ingress to route the traffic to an old domain to the nginx container and injects a custom "server" block to implement redirect rules:
ingress:
enabled: true
hostname: old-domain.tld
ingressClassName: nginx
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
tls: true
serverBlock: |-
server {
listen 0.0.0.0:8080;
location /pages/about {
return 301 https://new-domain.tld/about;
}
location / {
return 302 https://new-domain.tld/;
}
}
To install the chart one should run the following commands (assuming the file above is located in the current directory and named my-values.yaml
):
helm repo add bitnami https://charts.bitnami.com/bitnami
helm install redirector bitnami/nginx -f my-values.yaml
In this case, the ingress controller will proxy requests to the "redirector" instance of nginx and it will respond with appropriate redirect headers.
Ingress configuration to handle redirects
But why use an additional nginx container if you probably already have one in your cluster — your ingress controller? Is it possible to inject redirect configuration directly into it? The answer is yes, but there are several issues.
The only thing you need is the ingress resource looking something like this:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-redirector
annotations:
nginx.org/server-snippets: |
location /pages/about {
return 301 https://new-domain.tld/about;
}
location / {
return 302 https://new-domain.tld/;
}
spec:
ingressClassName: nginx
rules:
- host: "old-domain.com"
tls:
- hosts:
- "old-domain.com"
secretName: old-domain-cert
As you can see, the nginx configuration is injected via nginx.org/server-snippets
annotation and follows nginx configuration syntax. Otherwise, ingress doesn't look any different from a regular one, except there is no need for backend service configuration as all the traffic will be handled by the ingress controller itself.
The "issues" I mentioned are:
Your ingress controller has to be based on nginx (maybe other controllers also can be configured for the task but they are not covered in this blog post).
Snippets configuration should be enabled on the controller level. Either with
enable-snippets
command-line argument or, if you use helm to deploy your ingress controller, by settingcontroller.enableSnippets
parameter totrue
in yourvalues.yaml
file.Third, it introduces a certain risk: if the configuration snippet has invalid syntax it will block configuration refresh for all the services using this ingress controller in the cluster. Even other ingress configuration changes won't apply. See more in "Disadvantages of Using Snippets".
To reduce the risk of invalid configuration, you can use a public helm chart I've created: https://github.com/obukhov/ingress-redirector. Of course, it is still possible to break it with some special character in "from", "to" or "code" parameters. But at least it is harder to do it unintentionally and values.yaml
file will look a bit more friendly:
ingress:
hosts:
- host: old-domain.tld
tls:
- secretName: old-domain-tld-tls
hosts:
- old-domain.tld
redirectRules:
- from: /pages/about
to: https://new-domain.tld/about
code: 301
- from: /
to: https://new-domain.tld/
code: 302
Summary
What solution to choose depends on your situation. Do you have several development teams, and such implicit dependencies between their services in a shared runtime can cause an issue? Or is it just your pet project, and you really would like to avoid deploying an extra nginx container that will require resources? Kubernetes can serve your needs anyway.