My requests are being proxied through Cloudflare which sets a header indicating the country in a http header based on the IP address. I want to redirect the requests with certain paths based on this header in Nginx ingress controller. How do I do this?
-
1Hello, could you clarify if you are trying to "rewrite" the request by a header directed to one svc or are you trying to route this requests to the different svc's by header? Please take a look here: https://stackoverflow.com/questions/26223733/how-to-make-nginx-redirect-based-on-the-value-of-a-header . Also as more of a pointer that a solution, Traefik and Ambassador have an option to run header based routing. – Dawid Kruk Aug 04 '20 at 13:19
-
Thank you @Dawid. I’m looking to rewrite requests to bunch of SVCs based on header. The answer suggested in that question is how I’m doing it currently in Nginx. But was wondering how to achieve it in `Ingress` resource definitions. Looks like I need to define a map with redirect URLs in the `http-snippet` config in the Nginx Ingress Controller and then use either `nginx.ingress.kubernetes.io/configuration-snippet` or `nginx.ingress.kubernetes.io/server-snippet` in the `Ingress resource` to do the redirection. Didn’t know Traefik and Ambassador have this option. Thanks, will check them out. – Sudarshan Murthy Aug 05 '20 at 13:00
1 Answers
Currently Ingress
resource definition for nginx-ingress
does not support header based routing.
I found a workaround to route a request by it's header (I've included the steps below) with following annotation:
nginx.ingress.kubernetes.io/configuration-snippet: |
if ($http_LocationHeader = "PL") { proxy_pass http://goodbye-service.default.svc.cluster.local:5678; }
Other possible solutions/workarounds:
- Use
Traefik
: Docs.traefik.io: Routing: Routers - Use
Ambassador
: Getambassador.io: Docs: Using: Headers - Use
Istio
: Istio.io: Traffic management: Request routing - Route the traffic from
nginx-ingress
to nginx pod/deployment/daemonset with header based routing logic included: Stackoverflow.com: How to have a header routing logic with nginx ingress-controller? (last comment under the answer)
As for a workaround:
Assuming that (for example purposes):
- There are 2 Deployments:
hello
,goodbye
- Both are associated with their services with names:
hello-service
,goodbye-service
The Ingress
resource will be configured in a way that hello
should always answer, but with the addition of configuration-snippet
the traffic will be redirected to goodbye
.
Responses of this deployments:
| hello | goodbye |
|----------------|----------------|
| Hello, world! | Hello, world! |
| Version: 2.0.0 | Version: 1.0.0 | # notice the version
Example of hello
deployment with a service attached to it:
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello
spec:
selector:
matchLabels:
app: hello
replicas: 1
template:
metadata:
labels:
app: hello
spec:
containers:
- name: hello
image: "gcr.io/google-samples/hello-app:2.0"
env:
- name: "PORT"
value: "50001"
---
apiVersion: v1
kind: Service
metadata:
name: hello-service
spec:
selector:
app: hello
ports:
- name: hello-port
port: 5678 # IMPORTANT
targetPort: 50001
type: NodePort
To get the goodbye
deployment please substitute the hello
for goodbye
and change the image version to 1.0
.
Ingress definition to reroute the request by a header looks like this:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: hello-ingress
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/configuration-snippet: |
if ($http_LocationHeader = "PL") { proxy_pass http://goodbye-service.default.svc.cluster.local:5678; }
spec:
rules:
- host:
http:
paths:
- path: /
backend:
serviceName: hello-service
servicePort: hello-port
By default this Ingress definition without the configuration-snippet
would always route the traffic to hello-service
and then to hello
pods. By adding the:
nginx.ingress.kubernetes.io/configuration-snippet: |
if ($http_LocationHeader = "PL") { proxy_pass http://goodbye-service.default.svc.cluster.local:5678; }
it will check if the header named LocationHeader
is present and if it matches PL
. If it does it will send the request to goodbye-service
by it's DNS name.
Focusing on:
http://goodbye-service.default.svc.cluster.local:5678
http://service_name.namespace.svc.cluster.local:port
(dns name without values)
After applying this Ingress
resource you should be able to send a request with LocationHeader=PL
(with Postman for example) and get the response:
Hello, world!
Version: 1.0.0
Hostname: goodbye-5758448754-wr64c
When I tried to use
map
directive I was getting following messages:
nginx: [emerg] "map" directive is not allowed here in /tmp/nginx-OMMITED

- 8,982
- 2
- 22
- 45
-
Thank you @Dawid for the detailed answer. I was able to achieve this using `configuration-snippet`. As for the map, it is not allowed in `server` and `location` contexts which is why you were seeing the error. It has to be injected using `http-snippet` while deploying the NGINX ingress controller. – Sudarshan Murthy Aug 13 '20 at 05:50