ProxyPass

ProxyPass, ProxyPassReverse and Forwarded Headers

sudo a2enmod proxy_http enables mod_proxy and its HTTP backend. ProxyPass maps a URL prefix to a backend; ProxyPassReverse rewrites the Location, Content-Location and URI headers of its redirects. Keep ProxyRequests Off, or you run an open forward proxy.

A reverse proxy for one path, inside the virtual hostApache config
ProxyRequests Off
ProxyPass        "/app/" "http://127.0.0.1:9203/"
ProxyPassReverse "/app/" "http://127.0.0.1:9203/"
RequestHeader set X-Forwarded-Proto "expr=%{REQUEST_SCHEME}"
What the backend sees, and a backend redirect seen through the proxyShell
curl -s -H 'X-Forwarded-For: 203.0.113.9' http://127.0.0.1:8103/app/orders?id=7
curl -sI http://127.0.0.1:8103/app/login | grep -iE '^(HTTP|Location)'
Output
app1 saw: GET /orders?id=7
HTTP_HOST                127.0.0.1:9203
HTTP_X_FORWARDED_FOR     203.0.113.9, 127.0.0.1
HTTP_X_FORWARDED_HOST    127.0.0.1:8103
HTTP_X_FORWARDED_SERVER  localhost
HTTP_X_FORWARDED_PROTO   http
REMOTE_ADDR              127.0.0.1
HTTP/1.1 302 Found
Location: http://127.0.0.1:8103/app/dashboard

Apache 129 added three X-Forwarded-* headers itself (ProxyAddHeaders On); the protocol header, which frameworks need to build https:// links, came from RequestHeader. The backend's redirect to 127.0.0.1:9203/dashboard came out as the public URL. The pitfalls: the backend got Host: 127.0.0.1:9203, so add ProxyPreserveHost On if it routes by host name; the client's forged address survived at the front of X-Forwarded-For, so trust only the entry your own proxy appended (mod_remoteip with RemoteIPTrustedProxy); and the backend's PHP version leaks through (A Server Hardening Checklist). Apache does not emit the standard Forwarded header (RFC 7239).