Apache’s mod_proxy module turns Apache into a powerful reverse proxy and gateway, forwarding requests to backend application servers, other web servers, or balancer clusters. Unlike Nginx’s reverse proxy (which is native), Apache’s proxy functionality is modular: mod_proxy handles the core proxying, mod_proxy_http handles HTTP/1.1, mod_proxy_balancer provides load balancing, and mod_proxy_wstunnel handles WebSocket tunnelling. On RHEL 9, all these modules are included with the httpd package. This guide covers setting up a basic reverse proxy for a Node.js or Python app, SSL termination, load balancing with mod_proxy_balancer, WebSocket proxying, and passing the correct client IP headers to the backend.
Prerequisites
- Apache installed and running on RHEL 9
- A backend application on a local port (e.g., Node.js on port 3000)
Step 1 — Enable Required Modules
# Verify the proxy modules are loaded
httpd -M | grep proxy
# If not loaded, check the modules config
ls /etc/httpd/conf.modules.d/00-proxy.conf
cat /etc/httpd/conf.modules.d/00-proxy.conf
The following should be uncommented in 00-proxy.conf:
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
LoadModule lbmethod_byrequests_module modules/mod_lbmethod_byrequests.so
LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
apachectl configtest && systemctl reload httpd
Step 2 — Basic Reverse Proxy
vi /etc/httpd/conf.d/myapp.conf
ServerName app.example.com
# Disable forward proxy — only act as reverse proxy
ProxyRequests Off
ProxyPreserveHost On
# Pass real client IP to backend
RequestHeader set X-Forwarded-For "%{REMOTE_ADDR}e"
RequestHeader set X-Forwarded-Proto "http"
RequestHeader set X-Real-IP "%{REMOTE_ADDR}e"
ProxyPass / http://127.0.0.1:3000/
ProxyPassReverse / http://127.0.0.1:3000/
ErrorLog /var/log/httpd/myapp.error_log
CustomLog /var/log/httpd/myapp.access_log combined
# Allow Apache to connect to backends (SELinux)
setsebool -P httpd_can_network_connect 1
apachectl configtest && systemctl reload httpd
Step 3 — Load Balancing with mod_proxy_balancer
ServerName app.example.com
ProxyRequests Off
ProxyPreserveHost On
# Define the balancer cluster
BalancerMember http://127.0.0.1:3000 loadfactor=1
BalancerMember http://127.0.0.1:3001 loadfactor=1
BalancerMember http://127.0.0.1:3002 loadfactor=1
ProxySet lbmethod=byrequests
ProxyPass / balancer://myappcluster/
ProxyPassReverse / balancer://myappcluster/
Step 4 — WebSocket Proxy with mod_proxy_wstunnel
ServerName ws.example.com
ProxyRequests Off
# HTTP traffic
ProxyPass /api/ http://127.0.0.1:4000/api/
ProxyPassReverse /api/ http://127.0.0.1:4000/api/
# WebSocket traffic — must come before the HTTP rule
RewriteEngine On
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule /ws/(.*) ws://127.0.0.1:4000/ws/$1 [P,L]
ProxyPass /ws/ ws://127.0.0.1:4000/ws/
ProxyPassReverse /ws/ ws://127.0.0.1:4000/ws/
Step 5 — Reverse Proxy with SSL Termination
ServerName app.example.com
Redirect permanent / https://app.example.com/
ServerName app.example.com
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/app.example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/app.example.com/privkey.pem
ProxyRequests Off
ProxyPreserveHost On
RequestHeader set X-Forwarded-Proto "https"
ProxyPass / http://127.0.0.1:3000/
ProxyPassReverse / http://127.0.0.1:3000/
Verification Checklist
apachectl configtest
curl -I http://app.example.com
httpd -M | grep proxy
tail -f /var/log/httpd/myapp.error_log
Conclusion
Apache mod_proxy on RHEL 9 provides HTTP reverse proxying, load balancing across multiple backends, and WebSocket tunnelling. Combined with SSL termination and RequestHeader set X-Forwarded-Proto, your backend application receives accurate client information and the correct protocol signal.
Next steps: How to Enable HTTP/2 with Nginx on RHEL 9, How to Configure Nginx Load Balancing on RHEL 9, and How to Set Up Varnish Cache as a Reverse Proxy on RHEL 9.