← Back

2026

Reverse Proxy: The Layer in Front of Your Servers

A reverse proxy sits in front of your backends and handles every inbound request before it reaches them. TLS termination, load balancing, caching and access control all happen here. Here is how it works and why almost every production service uses one.

Almost every web service you use on a daily basis has a reverse proxy sitting in front of it. You will never see it. It does not appear in the URL. The certificate belongs to it rather than the server that actually built the response. It is there regardless, handling the connection before anything else gets involved.

A reverse proxy accepts incoming requests on behalf of one or more backend servers. It is configured by whoever runs the infrastructure, not by the client. Clients connect to it thinking they are connecting directly to the service. The proxy decides what happens next.

What a reverse proxy actually does

The request arrives at the reverse proxy's IP address. The proxy reads it, applies whatever rules are configured, then forwards it to a backend server. The backend produces a response. That response travels back through the proxy before reaching the client.

From the client's perspective, the conversation was with the service. There is no visible indication that anything forwarded the request. The TLS certificate matches the domain. The response headers look normal. The proxy is invisible by design.

From the backend's perspective, requests arrive from the proxy's address rather than from the original client. This matters for logging. Backend servers need extra headers to find out where a request actually came from, because the network layer will only show the proxy.

How a request moves through it

The path is straightforward. Each step has a defined role, which is what makes the model easy to reason about.

Request path through a reverse proxy

Client (browser, mobile app, API consumer)

↓ connects to the public IP or domain

Reverse proxy

↓ terminates TLS if HTTPS

↓ checks rules (rate limits, auth, routing)

↓ selects a backend server from the pool

↓ forwards request with extra headers

Backend server

↓ processes request, returns response to proxy

Reverse proxy

↓ optionally caches or compresses the response

↓ returns response to the client

Client receives response

The backend never needs a public IP. It can run on a private network that only the proxy can reach. Clients on the public internet have no path to the backend directly. Traffic must pass through the proxy to get there at all.

Why you would run one

Reverse proxies solve several distinct problems. Most production deployments use them for more than one reason at the same time.

Load balancing

When traffic arrives at a reverse proxy, the proxy decides which backend server handles each request. It can distribute load evenly across a pool, route based on server health, or use weighted rules that send more traffic to more capable machines. Individual servers can be restarted without the client ever knowing.

TLS termination

Handling TLS at the reverse proxy means backend servers receive plain HTTP. The certificate lives in one place. Renewals happen in one place. Backend servers do not need to be configured for TLS at all. This is the most common reason organisations reach for a reverse proxy even when they only have a single backend.

Caching

A reverse proxy can cache responses from backend servers and serve them directly to subsequent clients without touching the upstream at all. This reduces backend load for content that does not change between requests. Static assets are an obvious candidate. API responses with short but valid cache lifetimes benefit from it too.

Hiding backend topology

Clients connect to the reverse proxy's address. They never see backend hostnames, internal IPs or port numbers. Backend servers can be rearranged, replaced or scaled without any change visible to the outside world. This is why the architecture behind most production web services looks nothing like what a client would guess from the outside.

Rate limiting and access control

Because every inbound request passes through the reverse proxy, it becomes a natural enforcement point. Rules that limit requests per IP, require authentication headers, block specific paths or reject traffic from known bad actors can all sit here rather than being reimplemented in every backend service.

Compression

Compressing responses at the proxy level means backends serve uncompressed content and the proxy handles gzip or Brotli before bytes leave the data centre. Backend developers do not need to think about it. Clients that advertise support for compression receive smaller payloads automatically.

Headers a reverse proxy adds

Because the proxy forwards requests using its own connection to the backend, the backend would otherwise lose all information about the original client. A set of headers exists specifically to preserve that information.

X-Real-IP

Carries the originating client IP. Without this, backend logs would record the proxy's address for every request.

X-Forwarded-For

A comma-separated list of IPs representing the chain of proxies a request has passed through. The leftmost entry is the original client.

X-Forwarded-Proto

Tells the backend whether the original request arrived over HTTP or HTTPS. Backends that redirect HTTP to HTTPS need this to avoid redirect loops.

X-Forwarded-Host

The original Host header as the client sent it. The proxy may rewrite the Host header before passing it upstream, so this preserves what the client actually requested.

Backend applications reading these headers need to trust the proxy that sent them. If a client can set these headers directly and the backend believes them, that is a spoofing problem. The backend should only trust these headers if the connection comes from a known proxy address.

nginx as a reverse proxy

nginx is the most common reverse proxy in production environments. It handles TLS termination, load balancing, caching and header rewriting. A basic configuration that covers most of what a typical service needs is not very long.

upstream block

upstream backend_pool {
    server 10.0.1.10:3000;
    server 10.0.1.11:3000;
    server 10.0.1.12:3000;
}

Defines the pool of backend servers. nginx distributes requests across all three by default using round-robin.

server block

server {
    listen 443 ssl;
    server_name example.com;

    ssl_certificate     /etc/nginx/certs/example.com.crt;
    ssl_certificate_key /etc/nginx/certs/example.com.key;

    location / {
        proxy_pass http://backend_pool;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

TLS terminates here. The backend_pool receives plain HTTP. The X-Forwarded headers carry the original client information through.

The proxy_pass directive points to the upstream block by name. nginx resolves that name to the list of backend addresses defined there. Adding a new backend is a matter of adding a line to the upstream block. Removing one is the reverse. nginx picks up the change on reload without dropping existing connections.

Health checks

A load balancer without health checking will send requests to backends that are down. The client receives an error that the proxy could have avoided. Health checks are how the proxy knows which backends are worth sending traffic to.

The proxy periodically sends a request to each backend, typically to a lightweight endpoint like /health or /ping that returns a fast response. If a backend fails to respond within a timeout, the proxy marks it as unavailable. Traffic stops going to it. When the backend starts responding again, the proxy marks it healthy and traffic resumes.

upstream block with passive health checking

upstream backend_pool {

server 10.0.1.10:3000 max_fails=3 fail_timeout=30s;

server 10.0.1.11:3000 max_fails=3 fail_timeout=30s;

}

Passive health checking marks a backend unavailable after it produces a given number of failures within a time window. Active health checking, available in nginx Plus, probes backends continuously rather than waiting for a real request to fail. For most self-hosted setups, passive checking is sufficient. The behaviour is predictable and the configuration is simple.

Where it fits

A reverse proxy shows up in almost every production deployment because it solves several infrastructure problems at once without requiring changes to the application. TLS, load balancing, caching and rate limiting all become configuration rather than code.

Small services often start with a single backend behind a reverse proxy not because they need load balancing yet, but because TLS termination in one place is already worth it. The load balancing comes later when a second server gets added to the upstream block. The client never notices either step.

Once you have seen the request path laid out, most reverse proxy configuration becomes readable quickly. Every directive is describing one of three things: where to send traffic, what to check before sending it, or what to do with it on the way through.