Ingress Controller
Proxy Pass
User Agent
Load Balancing
Kubernetes

Ingress controller - proxy pass based on user agent

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

Routing traffic by User-Agent is possible in Kubernetes, but the answer depends on which ingress controller you run. The standard Ingress API only understands hosts and paths. If you want request routing based on headers such as User-Agent, you are already in controller-specific territory, and with NGINX that usually means snippets, rewrites, or a dedicated proxy configuration.

Understand the boundary of standard Ingress

A normal Ingress resource does not have a field for "send iPhone clients to service A and desktop clients to service B." It describes HTTP routing at a higher level:

  • host matching
  • path matching
  • TLS configuration
  • backend services

That means raw proxy_pass logic is not something the Kubernetes API models directly. The ingress controller generates its own NGINX configuration from your resource, and direct low-level routing tricks may or may not fit cleanly into that generated config.

A practical ingress-nginx pattern

With ingress-nginx, a common pattern is to rewrite matching requests to an internal path and let normal path-based routing choose the backend service. That keeps you closer to supported behavior than trying to override the controller's generated proxy_pass line.

yaml
1apiVersion: networking.k8s.io/v1
2kind: Ingress
3metadata:
4  name: ua-router
5  annotations:
6    nginx.ingress.kubernetes.io/server-snippet: |
7      if ($http_user_agent ~* "(iphone|android|mobile)") {
8        rewrite ^ /mobile$uri last;
9      }
10spec:
11  ingressClassName: nginx
12  rules:
13    - host: example.com
14      http:
15        paths:
16          - path: /mobile
17            pathType: Prefix
18            backend:
19              service:
20                name: mobile-backend
21                port:
22                  number: 80
23          - path: /
24            pathType: Prefix
25            backend:
26              service:
27                name: web-backend
28                port:
29                  number: 80

Here the request still arrives at one host. Mobile-style user agents are rewritten to /mobile..., and the generated NGINX locations for the Ingress send them to mobile-backend. Everyone else stays on the default path and reaches web-backend.

This is usually easier to reason about than injecting your own upstream selection logic into the controller's internals.

If you truly need raw proxy_pass

Sometimes you want exact NGINX behavior, not just ingress-nginx annotations. In that case, a dedicated NGINX deployment or a controller-specific custom resource may be the better tool.

A plain NGINX example looks like this:

nginx
1map $http_user_agent $backend {
2    default http://web-backend.default.svc.cluster.local:80;
3    ~*(iphone|android|mobile) http://mobile-backend.default.svc.cluster.local:80;
4}
5
6server {
7    listen 8080;
8
9    location / {
10        proxy_pass $backend;
11        proxy_set_header Host $host;
12        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
13    }
14}

That config is real NGINX and does true upstream selection by user agent. The tradeoff is that you are no longer relying only on the portable Ingress abstraction.

Operational concerns matter here

Before reaching for user-agent routing, ask whether it belongs at the edge at all. If mobile and desktop clients can share the same endpoint with capability negotiation inside the application, that is often simpler.

If you still need edge routing, verify these details:

  • snippet annotations may be disabled by cluster policy
  • controller upgrades can affect custom snippets
  • regex matching should be tested against actual user-agent strings
  • rewrite-based routing needs the backend services to understand the rewritten paths

Also remember that User-Agent is easy to spoof. It is fine for product segmentation or gradual migration, but it is not a security boundary.

Common Pitfalls

The most common mistake is assuming the Ingress spec itself supports header-based backend selection. It does not.

Another mistake is dropping a raw proxy_pass into a snippet without understanding that ingress-nginx already generates its own locations and upstreams. That can produce invalid or fragile NGINX configs.

Teams also forget that snippets are often turned off for security reasons, especially in multi-tenant clusters.

Finally, do not rely on User-Agent for authentication, authorization, or bot protection. It is a hint, not a trustworthy identity signal.

Summary

  • Standard Kubernetes Ingress does not directly express user-agent-based backend routing.
  • In ingress-nginx, a rewrite-to-path pattern is often safer than forcing custom proxy_pass logic.
  • If you need exact NGINX behavior, use a dedicated proxy config or controller-specific extension.
  • Test cluster policy, regex behavior, and rewritten paths before rollout.
  • Treat User-Agent as a routing hint, not a security control.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.