Decoupling Your Backend: Implementing a Reverse Proxy with Apache mod_proxy
Learn how to use Apache mod_proxy to decouple frontend clients from backend servers, enabling SSL termination and preserving client identity with X-Forwarded-For headers.
21 Jan 2026, 05:18 UTC

The Backend Exposure Problem
Exposing an application server directly to the public internet creates a fragile architecture. When your backend handles both business logic and raw TLS (Transport Layer Security) handshakes, you consume precious CPU cycles on encryption and risk exposing internal server signatures to attackers. Furthermore, any change to the backend IP or port requires a DNS update or client-side configuration change, leading to potential downtime.
The solution is to place an Apache HTTP Server in front of your application as a reverse proxy. By using mod_proxy, Apache acts as the single point of entry, handling SSL termination and request routing, while your application servers remain isolated in a private network. This decouples the client's connection from the server's physical location.
Core Directives for Request Routing
To function as a proxy, Apache relies on two primary directives within the mod_proxy and mod_proxy_http modules: ProxyPass and ProxyPassReverse.
- ProxyPass: Maps a local URL path to a remote server. For example, any request coming to
/apiis forwarded to a backend server running on a different port or IP. - ProxyPassReverse: This is critical for handling HTTP redirects. If the backend server sends a 301 or 302 redirect to the client,
ProxyPassReverserewrites theLocationheader so the client is redirected back to the proxy rather than attempting to connect to the internal backend IP directly.
Preserving Client Identity
When a request passes through a proxy, the backend server sees the proxy's IP address as the source, not the original client's. This breaks logging, geolocation, and security rate-limiting. To fix this, you must inject identity headers using mod_headers.
The X-Forwarded-For header carries the original client IP, and X-Forwarded-Proto tells the backend whether the original request was HTTP or HTTPS. This allows the backend to generate correct absolute URLs for emails or redirects without needing to know the proxy's specific configuration.
Implementation Example: Proxying a Node.js App
Assume an Apache server (Ubuntu/Debian) acting as the frontend and a Node.js application running on an internal server at 10.0.0.50:3000.
1. Enable Required Modules
Run these commands as root or with sudo on the Apache server to activate the proxy engine:
sudo a2enmod proxy
sudo a2enmod proxy_http
sudo a2enmod headers
sudo systemctl restart apache2
2. Configure the Virtual Host
Add the following configuration to your site's VirtualHost file (e.g., /etc/apache2/sites-available/000-default.conf):
<VirtualHost *:80>
ServerName app.example.com
# Preserve the original client IP and protocol
RequestHeader set X-Forwarded-Proto "http"
RequestHeader set X-Forwarded-Port "80"
# Route all traffic to the backend Node.js server
ProxyPass / http://10.0.0.50:3000/
ProxyPassReverse / http://10.0.0.50:3000/
# Error logging for proxy failures
ErrorLog ${APACHE_LOG_DIR}/proxy_error.log
CustomLog ${APACHE_LOG_DIR}/proxy_access.log combined
</VirtualHost>
3. Verification
Check that the modules are active by running apachectl -M | grep proxy. To verify the identity headers are working, check the backend application logs; you should see the X-Forwarded-For header containing the external client's IP address rather than the Apache server's internal IP.
Trade-offs and Limitations
While mod_proxy provides significant flexibility, it introduces a single point of failure. If the Apache instance crashes, all backend services become unreachable regardless of their health. To mitigate this, you would need a High Availability (HA) pair using Keepalived or a cloud load balancer in front of the proxy.
Additionally, be cautious with timeout settings. If your backend application performs long-running tasks (like generating large reports), the default Apache proxy timeout may trigger a 504 Gateway Timeout. You can adjust this using the timeout parameter within the ProxyPass directive (e.g., ProxyPass / http://backend:3000/ timeout=60), but setting this too high can lead to worker exhaustion during backend latency spikes.
Actionable Summary
To successfully decouple your frontend and backend, ensure you always pair ProxyPass with ProxyPassReverse to avoid redirect loops. Always implement X-Forwarded-For headers to maintain visibility into your traffic. Finally, monitor your proxy_error.log specifically for "service unavailable" errors, which typically indicate a network partition or a crash on the backend application server.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.