Theo's Corner
dev / irl / thoughts
← Back
tech

What a reverse proxy actually does — a deeper dive

Theo|Jul 2026|~5 min read

I touched on reverse proxies in the nginx post. This one goes deeper — what's actually happening, why it matters, and some of the less obvious things you can do with one.

Forward proxy vs reverse proxy

A forward proxy sits in front of clients — it's what corporate networks use to filter outgoing traffic, or what VPNs do. You connect to the proxy, the proxy connects to the internet on your behalf. The server sees the proxy's IP, not yours.

A reverse proxy is the opposite — it sits in front of servers. Clients connect to the proxy, the proxy connects to the backend server. The client sees the proxy's IP, not the server's. nginx running as a reverse proxy is the standard setup for almost everything I host.

What actually happens in the request cycle

A request comes in to nginx on port 443. nginx terminates the TLS connection — it decrypts the HTTPS traffic using the SSL certificate. It then makes a new HTTP connection to the backend (your app on port 3000, or wherever) and forwards the request. The backend responds, nginx takes the response, and sends it back to the client over the encrypted connection.

This means your backend app doesn't need to handle SSL at all. nginx handles it. Your app just gets plain HTTP requests on an internal port. Simpler, cleaner, and you only have one place to manage certificates.

Header manipulation

When nginx forwards a request it can add or modify headers. The most important ones:

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;

Without these, your backend sees nginx's localhost IP as the client IP and doesn't know whether the original request was HTTP or HTTPS. With them, it gets the real client IP and the original protocol.

Multiple backends from one nginx

One nginx instance can proxy to multiple completely different backends based on the domain name in the request. myserverhost.example.com goes to one app, panel.myserverhost.example.com goes to another, blog.citycraftmc.com goes to another. nginx reads the Host header, matches it to a server block, and routes accordingly. One server, many services, all on standard ports.

WebSocket proxying

WebSockets need special handling through a reverse proxy — they're long-lived connections that need to be upgraded from HTTP. The extra config is small:

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

Pterodactyl uses WebSockets for the real-time console in the panel. Getting this wrong means the console doesn't work. Getting it right means it just works seamlessly.