The idea: the same relay, on opposite sides

A proxy is a program that accepts your connection, opens a new connection of its own to the next machine, and copies the request and the reply across. Because the second connection starts at the proxy, the far side always sees the proxy's address as the source, never yours. A forward proxy and a reverse proxy are the same machine in that sense. What differs is whose side it is on. The top lane on the canvas is an office whose employees reach the Internet through the company's proxy; the bottom lane is a shop whose visitors reach its apps through the shop's proxy. Every Send runs the same request through both, one message per step.

Two rows. Forward proxy: Alice 10.0.0.5 connects to corp-proxy 10.0.0.1:3128, which connects from 198.51.100.7 to news.example 203.0.113.80; news.example sees source 198.51.100.7 and never learns Alice's address. Reverse proxy: Carol 192.0.2.25 connects to 203.0.113.10, the address of shop.example, which is nginx; nginx connects from 10.1.0.2 to app1 10.1.0.11:8080; Carol never learns app1's address.
Each proxy hides the side that runs it: the office's clients from the sites, the shop's apps from the visitors.

Forward proxy: works for the clients

A forward proxy is run by the clients' side: a company, a school, an ISP. The client knows it is there, because it is configured to use it (a proxy setting, a PAC file, or WPAD). Every request leaves the network through it, so it is the place to log who went where, block sites (an ACL on the host name), and, for plain HTTP, cache pages for everyone. A plain HTTP request to a proxy carries the whole URL, GET http://news.example/logo.png, so the proxy can read and cache it (Demo: cache over plain HTTP).

HTTPS changes this. The browser sends only CONNECT news.example:443, the proxy answers 200 Connection established, and from then on it copies encrypted bytes it cannot read. It still sees the host name, so it can still block games.example, but it cannot see the path, cannot cache and cannot add headers (Demo: one HTTPS page through each, Demo: office: two people, one blocked site). Companies that want more install their own root certificate on every laptop and decrypt the traffic ("TLS inspection"); this page leaves that out.

Reverse proxy: works for the site

A reverse proxy is run by the site. The client does not know it is there: DNS gives the proxy's address for shop.example, and the client thinks it is talking to the server. Behind it, the real servers sit on a private network the Internet cannot reach. Because the proxy holds the site's certificate, TLS ends there (TLS termination) and it reads every request. That lets it pick a backend (load balancing, Demo: TLS termination + load balancing), serve repeated files from its cache (Demo: cache /logo.png over HTTPS), refuse paths such as /admin, and speak plain HTTP to the apps. nginx, HAProxy, Envoy, Traefik and every CDN edge are reverse proxies.

Top: with a forward proxy and HTTPS, the browser sends CONNECT news.example:443, then one TLS session runs from the browser through the proxy to news.example; the proxy sees only the host name and encrypted bytes. Bottom: with a reverse proxy, TLS runs from the browser to nginx only; nginx decrypts, reads GET /logo.png, can answer from its cache, and sends plain HTTP to app1.
For HTTPS a forward proxy is a blind tunnel; a reverse proxy ends TLS, so it can read, cache and route.

X-Forwarded-For: a leak on one side, a feature on the other

Since the far side only sees the proxy's address, proxies can add a header with the original one: X-Forwarded-For: 192.0.2.25 (or the standard Forwarded: for=192.0.2.25, RFC 7239). Behind a reverse proxy that is what you want: the app needs the visitor's address for logs and rate limits, and in nginx you add it with proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;. From a forward proxy the same header leaks the employee's address to every site, which defeats the point. Squid adds it by default (forwarded_for on); forwarded_for delete removes it. Over HTTPS a forward proxy cannot add it at all (Demo: X-Forwarded-For: leak vs feature).

Each proxy appends to the header, so it can hold a list. Anyone can send a fake first entry, so an app must trust only the entries added by its own proxies: count from the right (nginx real_ip_header with set_real_ip_from).

Which one?

Forward proxyReverse proxy
Run bythe clients' side (office, school, ISP)the site
Client knows about ityes, it is configuredno, it looks like the server
Hidesthe clients from the sitesthe backends from the clients
HTTPSCONNECT tunnel: sees the host onlyterminates TLS: sees everything
Typical jobsegress filtering, logging, shared cache, one exit addressTLS, load balancing, caching, path rules, compression, DDoS shield
X-Forwarded-Forusually removed (privacy)usually added (the app needs it)
ExamplesSquid, corporate web gateways, Tor exitsnginx, HAProxy, Envoy, CDNs, API gateways

See also how nginx handles a request, API gateway (a reverse proxy with auth and routing rules), load balancer, NAT (rewrites addresses in packets instead of opening a new connection) and VPN (hides your address like a forward proxy, but for all traffic, one layer down).

Which responses a proxy may keep, and for how long, is set by Cache-Control; see HTTP caching.

What the page leaves out

One request at a time, with TLS shown as single messages: the real handshake, TCP setup and keep-alive connections are left out. It leaves out TLS inspection by forward proxies, transparent (intercepting) proxies that clients are not configured for, SOCKS proxies, PAC/WPAD discovery, proxy authentication (407), cache validation with ETags, health checks and other load-balancing algorithms, and the PROXY protocol that passes the client address at the TCP level.