Preserve Client IPs and Fix Redirects with Apache Reverse Proxy
Configure Apache httpd 2.4 mod_proxy to forward client IPs and rewrite backend redirect locations safely.
23 Oct 2025, 17:47 UTC

The problem: lost client IPs and internal redirects
Your backend application logs every request as coming from the same address: your Apache server. When the application sends a redirect, the browser is pointed at an internal port such as 8080 instead of the public site name. This usually happens when Apache forwards traffic as a reverse proxy but does not identify the original client or rewrite the backend’s redirect headers.
The useful takeaway is straightforward: load mod_proxy and mod_proxy_http, enable ProxyPreserveHost, forward the original client IP in X-Forwarded-For, and use ProxyPassReverse so redirect Location headers point back through Apache.
How the modules fit together
mod_proxy is the base proxying module in Apache HTTP Server 2.4. mod_proxy_http adds the ability to forward HTTP requests to a backend application server. A reverse proxy accepts requests from the public internet, forwards them to the backend, and returns the response as though Apache served it directly.
You also need mod_headers to set forwarding headers.
Virtual host configuration
This example assumes Apache httpd 2.4 and a backend application running on backend.internal:8080.
<VirtualHost *:80>
ServerName app.example.com
ProxyRequests Off
ProxyPreserveHost On
<Location /app>
ProxyPass http://backend.internal:8080/app
ProxyPassReverse http://backend.internal:8080/app
RequestHeader set X-Forwarded-For "%{REMOTE_ADDR}h"
</Location>
</VirtualHost>
ProxyRequests Offkeeps Apache from operating as an open forward proxy, which is a serious security risk.ProxyPreserveHost Onsends the originalHostheader to the backend, which matters for virtual-host-aware applications.RequestHeader set X-Forwarded-Fortells the backend the original client IP.setreplaces any existing value; useaddif another proxy already appends addresses.ProxyPassReverserewritesLocationheaders in backend redirects so the browser stays on the public URL path.
Place more specific ProxyPass rules before general ones. Apache evaluates them in config order, and the first match wins.
Verify before you reload
- Run
sudo apachectl configtest(orhttpd -t) on the Apache host. You need root or sudo privileges. ExpectSyntax OK. If it fails, do not reload. - Confirm the required modules are loaded with
sudo apachectl -M | grep proxyandsudo apachectl -M | grep headers. Look forproxy_module,proxy_http_module, andheaders_module. - Reload Apache with your distribution’s service command, for example
sudo systemctl reload apache2orsudo systemctl reload httpd. This changes server state. - Test with
curl -v http://app.example.com/app/login. Inspect the response for aViaheader added by Apache and confirm any redirectLocationheader uses the public path, not the backend’s internal port. - Check the backend access log, for example
tail -f /var/log/backend/access.log. Requests should show the original client IP if the backend is configured to readX-Forwarded-For.
Limitations and trade-offs
X-Forwarded-Forcan be forged by a client if Apache blindly trusts an incoming value. Setting it fromREMOTE_ADDRcaptures the immediate client, which is safe at the edge, but be careful chaining multiple proxies.- The backend must trust Apache as a proxy and be configured to read forwarding headers. Otherwise, logs still show Apache’s IP.
- Timeout and keepalive mismatches between Apache and the backend can produce
502 Bad Gatewayerrors or dropped connections. TuneProxyPasstimeout parameters or backend keepalive settings if this occurs.
Rollback
Because editing the virtual host changes server state, keep a backup copy of the configuration file before editing. If the reload causes errors, restore the backup, run apachectl configtest, and reload again.
Closing
A reverse proxy works best when the backend knows who the client is and where the public entry point lives. After applying the configuration above, verify with curl -v and backend logs before treating the change as complete.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.