Load Balancing Algorithms: L4 vs L7 Explained
Load balancing algorithms decide which healthy backend should receive each connection or request. That decision sits underneath websites, APIs, container platforms, and distributed systems, so understanding load balancing algorithms helps explain why a service remains available under changing traffic, why sessions sometimes break, and why a fast algorithm can still overload one server. This guide compares Layer 4 (L4) and Layer 7 (L7) load balancing, explains the common algorithms, and shows how to choose and verify one in practice.
What Is Load Balancing?
A load balancer is a traffic-distribution component between clients and a pool of backend servers. Instead of every client selecting a server directly, the client connects to one stable address. The load balancer checks which backends are available, applies a selection algorithm, and forwards traffic to the chosen destination.
The word “load” can mean more than request count. A useful policy may consider active connections, response time, server weight, geographic location, or a hash of client information. The right choice depends on whether traffic is short-lived or long-lived, whether requests cost roughly the same, and whether the application keeps state locally.
Load balancers commonly provide four related functions:
- Distribution: select a backend using an algorithm.
- Health detection: stop sending traffic to failed or unhealthy instances.
- Connection management: accept, reuse, or terminate TCP and TLS connections.
- Policy enforcement: apply routing, TLS, access, or observability rules.
The distribution decision is the focus here. A load balancer does not make an application stateless by itself; it only makes it easier to place multiple instances behind one endpoint.
L4 and L7 Load Balancing
The key difference between L4 and L7 is what the balancer can see.
Layer 4: Transport-Level Balancing
An L4 load balancer operates on transport protocols such as TCP or UDP. It can inspect addresses, ports, connection state, and sometimes transport metadata, but it generally does not parse HTTP methods, paths, cookies, or response status codes. It chooses a backend for a connection or flow and forwards packets or byte streams.
This approach is efficient and protocol-agnostic. It can balance HTTPS without decrypting it, as well as database connections, SSH, SMTP, and custom TCP protocols. The limitation is that the balancer cannot distinguish /checkout from /images, or a cheap request from an expensive one, when both use the same connection.
Layer 7: Application-Level Balancing
An L7 load balancer understands an application protocol, commonly HTTP. It can route based on hostnames, URL paths, headers, cookies, methods, or content type. It can terminate TLS, add forwarding headers, retry certain failures, and observe HTTP response codes.
L7 control costs CPU, memory, and operational complexity because the proxy must parse requests and often maintain more application-aware state. It is usually the better fit for web APIs and microservices when routing policy matters. It is not automatically better: transparent TCP forwarding, very high packet rates, or non-HTTP protocols may favor L4.
The HTTP Semantics specification defines the methods, status codes, fields, and semantics that an HTTP-aware proxy can interpret. At L4, those details remain opaque bytes.
| Feature | L4 load balancing | L7 load balancing |
|---|---|---|
| Primary view | TCP/UDP flow, address, port | HTTP request and response |
| Typical protocols | TCP, UDP, TLS passthrough, databases | HTTP/1.1, HTTP/2, WebSocket, gRPC-aware proxies |
| Routing inputs | IP, port, connection, flow hash | Host, path, header, cookie, method |
| TLS handling | Often passes encrypted traffic through | Can terminate and re-encrypt TLS |
| Cost per decision | Lower | Higher |
| Failure visibility | Connection-level failure | HTTP status, timeout, and application signals |
| Best use | Generic or high-throughput transport | Web routing and service-aware policies |
| Main limitation | Cannot distinguish requests inside a flow | More proxy overhead and protocol assumptions |
Some systems combine both layers. An L4 network load balancer can provide a public address and preserve protocol transparency, while an L7 reverse proxy behind it routes HTTP requests to individual services. The layers should be understood as complementary rather than competing products.
Common Load Balancing Algorithms
An algorithm is only part of the policy. Health checks, connection reuse, retry rules, weights, and capacity limits can change the outcome substantially.
Round Robin
Round robin sends successive eligible connections or requests to backends in sequence: A, B, C, then A again. It is simple, predictable, and a good default when servers have similar capacity and requests have similar cost.
Basic round robin counts assignments, not work completed. If one request takes ten seconds and another takes ten milliseconds, the algorithm may still assign them evenly. With HTTP keep-alive, the exact behavior also depends on whether the proxy balances individual requests or pins a connection to one backend.
Weighted Round Robin
Weighted round robin gives stronger servers a larger share. A backend with weight 3 may receive about three times as many assignments as a backend with weight 1, assuming both remain healthy. This is useful during a gradual rollout, when instance sizes differ, or when one region has less capacity.
Weights are a capacity estimate, not a substitute for measurement. If traffic patterns change, static weights can become inaccurate.
Least Connections
Least connections selects the backend with the fewest active connections. It adapts better than round robin when connections have different lifetimes, such as long polling, WebSockets, or slow uploads.
The algorithm assumes active connection count correlates with work. That can be false when one backend has many idle keep-alive connections or when requests have very different CPU and database costs. Some implementations use a weighted variant so larger servers can hold more connections.
Least Response Time
Least-response-time policies use observed latency, often together with active connections, to prefer backends responding quickly. They can react to overloaded or degraded instances without waiting for a hard health-check failure.
Latency-based decisions need careful tuning. A temporary slow request can cause traffic to move away from a backend, while measurement windows and retries can amplify oscillation. Use percentiles and smoothing rather than treating one sample as truth.
IP Hash and Cookie-Based Affinity
IP hash maps a client address to a backend, making repeat connections from the same address likely to reach the same server. Cookie-based affinity writes an application-visible or proxy-managed cookie that identifies the chosen backend.
Affinity can help legacy applications that store sessions in process memory. It reduces flexibility, however: users behind one NAT address can concentrate on one server, and a failed backend forces remapping. Prefer a shared session store or stateless authentication when possible. Affinity is a compatibility tool, not a replacement for correct state management.
Consistent Hashing
Consistent hashing maps a key, such as a cache key or tenant ID, onto a hash ring. When a backend is added or removed, fewer keys move than with a simple modulo hash. This makes it useful for cache locality and partition-aware services.
The key must distribute well. A low-cardinality or skewed key can overload one backend. Hashing also does not guarantee that the selected backend has capacity, so health and overload protection remain necessary.
Random and Power of Two Choices
Random selection is inexpensive and, across a large number of assignments, can distribute traffic well. A refinement called “power of two choices” samples two eligible backends and selects the one with fewer active connections or lower load. It approaches the quality of global least-connections decisions without requiring a globally synchronized view.
The best algorithm is therefore workload-specific: round robin for uniform short requests, least connections for uneven connection lifetimes, and hashing for locality or compatibility requirements.
How Health Checks and Failures Change Distribution
A healthy backend is one that passes the configured check, not merely one whose process exists. Checks may open a TCP connection, request an HTTP endpoint, validate a status code, or test a deeper dependency. A shallow check can report success while the application cannot reach its database; a deep check can remove every backend when a shared dependency has a temporary problem.
Useful health-check controls include:
- Interval: how often to probe.
- Timeout: how long to wait for a response.
- Failure threshold: consecutive failures required for removal.
- Recovery threshold: consecutive successes required for re-entry.
- Draining: stop new traffic while allowing existing connections to finish.
When a backend fails, new requests should be sent elsewhere. Existing TCP connections may still fail, and an L7 proxy may retry only requests that are safe to repeat. Retrying a GET is usually safer than automatically repeating a payment POST; idempotency keys and application-level safeguards matter. A load balancer can reduce the blast radius of failure, but it cannot make every operation safe to replay.
Practical Configuration and Verification
The NGINX HTTP load-balancing documentation describes round robin as the default and documents alternatives such as least_conn, ip_hash, and weighted servers. This example routes HTTP requests at L7:
upstream app_pool {
least_conn;
server app-a:8080 weight=2 max_fails=3 fail_timeout=10s;
server app-b:8080 weight=1 max_fails=3 fail_timeout=10s;
}
server {
listen 80;
location / {
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://app_pool;
}
}
For a TCP service, an L4-capable proxy can balance without parsing application data. HAProxy’s project homepage provides the implementation and documentation entry point:
frontend postgres_frontend
bind :5432
mode tcp
default_backend postgres_pool
backend postgres_pool
mode tcp
balance leastconn
option tcp-check
server db-a 10.0.0.11:5432 check
server db-b 10.0.0.12:5432 check
Validate a configuration before reloading it:
nginx -t
sudo systemctl reload nginx
curl -i http://example.test/health
Then verify behavior rather than assuming that a successful reload proves correct distribution. Add an instance identifier to a non-sensitive response header or response body in a test environment, send repeated requests, and inspect access logs. For connection-oriented traffic, use ss -tn, proxy connection metrics, and a controlled client test. During a load test, record request rate, P50, P95, and P99 latency, error rate, active connections, retries, and backend saturation.
Choosing L4 or L7
Choose L4 when the service uses TCP or UDP, encrypted traffic should remain opaque to the balancer, packet and connection throughput is the priority, or the protocol is not understood by an HTTP proxy. It is also a useful first tier in front of multiple application proxies.
Choose L7 when you need host- or path-based routing, TLS termination, cookie or header policies, application-aware health checks, request metrics, or controlled retries. An API gateway is a specialized L7 layer with additional concerns such as authentication, rate limiting, and protocol mediation; the API gateway design patterns guide covers those responsibilities.
Whether a backend needs session affinity is an application-state decision, not a load-balancing algorithm. Prefer interchangeable backends with shared or externalized state when possible; the stateless versus stateful architecture guide explains the trade-offs.
For containerized deployments, the balancer must also account for changing endpoints and readiness. Service discovery, probes, and network policy determine whether a backend should receive traffic; the container networking guide explains those underlying mechanisms. In a growing microservice system, load balancing is one part of a broader scaling design that also includes stateless services, queues, caching, and autoscaling, as described in the microservices scalability guide.
Common Misconceptions
“Round robin always distributes load evenly”
It distributes assignments evenly under specific assumptions. Unequal request cost, long-lived connections, different server capacities, and connection reuse can all produce uneven work. Measure backend utilization and latency.
“L7 is always better than L4”
L7 provides more policy, but parsing and terminating traffic costs resources and introduces protocol-specific behavior. L4 is often the correct choice for generic, encrypted, or non-HTTP traffic.
“A passing health check means the service is healthy”
A check only tests the condition it was designed to test. Combine a lightweight readiness endpoint with application metrics and alerts. Avoid making the load balancer the only source of truth about system health.

