HTTP routing by header and path is how L7 load balancers decide which backend service should handle an incoming request.

Let’s watch this in action. Imagine we have two backend services: service-a and service-b.

# Request 1:
GET /api/v1/users HTTP/1.1
Host: api.example.com
X-Service-Name: service-a

# Request 2:
GET /api/v2/products HTTP/1.1
Host: api.example.com
X-Service-Name: service-b

Our L7 load balancer, configured with rules, will inspect these requests.

The first request, GET /api/v1/users to api.example.com with the header X-Service-Name: service-a, will be routed to service-a.

The second request, GET /api/v2/products to api.example.com with the header X-Service-Name: service-b, will be routed to service-b.

This allows us to split traffic based on very specific criteria, not just the destination IP and port.

The core problem this solves is managing microservices or different versions of an application behind a single entry point. Instead of exposing each service on a unique IP and port, we can funnel everything through one load balancer and let it intelligently direct traffic.

Internally, an L7 load balancer, also known as a reverse proxy or Application Load Balancer (ALB), operates at the application layer (Layer 7) of the OSI model. It terminates the client’s HTTP connection and then establishes a new connection to one of the backend servers. During this process, it can inspect the request’s contents: the URL path, HTTP headers, query parameters, and even the request body. Based on pre-defined rules, it selects the most appropriate backend server.

The exact levers you control are typically defined in rules or policies. For path-based routing, you’d specify a path prefix. For header-based routing, you’d specify an exact header name and a value to match.

Consider a rule like this in an Nginx configuration:

server {
    listen 80;
    server_name api.example.com;

    location /api/v1/users {
        proxy_pass http://service-a-cluster;
    }

    location /api/v2/products {
        proxy_pass http://service-b-cluster;
    }

    location / {
        if ($http_x_service_name = "service-a") {
            proxy_pass http://service-a-cluster;
        }
        if ($http_x_service_name = "service-b") {
            proxy_pass http://service-b-cluster;
        }
        # Default or fallback
        proxy_pass http://default-service;
    }
}

Here, location /api/v1/users directly matches requests starting with that path. The if blocks within the root location demonstrate header-based routing where the X-Service-Name header dictates the destination. The proxy_pass directive is what actually sends the request to the specified upstream group of servers.

The most surprising thing is that you can route based on arbitrary headers you invent yourself, not just standard ones like Host or User-Agent. This is incredibly powerful for service discovery and versioning within your application.

The next concept you’ll likely encounter is weighted routing, where you can send a percentage of traffic to different backend versions or services.

Want structured learning?

Take the full Load-balancing course →