How to Configure Apache mod_proxy as a Reverse Proxy on RHEL 7

Apache’s mod_proxy module transforms your existing Apache web server into a fully capable reverse proxy without requiring any additional software. On RHEL 7, Apache is the default web server and is frequently already running for other purposes. Rather than installing a second web server, you can use mod_proxy alongside mod_proxy_http to forward requests to backend applications such as Node.js, Tomcat, Gunicorn, or any HTTP service listening on a local port. This guide covers enabling the required modules, writing the proxy directives, forwarding client headers with mod_headers, resolving the SELinux policy that blocks outbound connections, and testing the complete setup.

Prerequisites

  • RHEL 7 with Apache (httpd) installed and running
  • A backend HTTP application listening on a local port (port 8080 is used in examples)
  • Root or sudo access
  • SELinux in enforcing mode (default on RHEL 7 — covered in the SELinux step)

Step 1: Install Apache and Verify Required Modules

If Apache is not already installed:

sudo yum install -y httpd
sudo systemctl enable httpd
sudo systemctl start httpd

The proxy modules are included in the base httpd package on RHEL 7. Verify they are present:

ls /etc/httpd/conf.modules.d/
# You should see: 00-proxy.conf among others

cat /etc/httpd/conf.modules.d/00-proxy.conf

The file should already contain load directives for mod_proxy, mod_proxy_http, mod_proxy_ajp, and others. Check that the critical lines are present and not commented out:

grep -E 'mod_proxy.so|mod_proxy_http.so' /etc/httpd/conf.modules.d/00-proxy.conf

Expected output:

LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so

If either line is commented out, open the file and remove the leading #:

sudo sed -i 's/^#LoadModule proxy_module/LoadModule proxy_module/' 
    /etc/httpd/conf.modules.d/00-proxy.conf
sudo sed -i 's/^#LoadModule proxy_http_module/LoadModule proxy_http_module/' 
    /etc/httpd/conf.modules.d/00-proxy.conf

Also ensure mod_headers is loaded — it is needed to forward client IP headers:

grep 'mod_headers' /etc/httpd/conf.modules.d/00-base.conf

Step 2: Enable the mod_headers Module

Open /etc/httpd/conf.modules.d/00-base.conf and verify the headers line is not commented:

LoadModule headers_module modules/mod_headers.so

If it is commented out, uncomment it:

sudo sed -i 's/^#LoadModule headers_module/LoadModule headers_module/' 
    /etc/httpd/conf.modules.d/00-base.conf

Step 3: Create a Reverse Proxy Virtual Host Configuration

Create a new configuration file for your proxied site:

sudo nano /etc/httpd/conf.d/myapp-proxy.conf

Add the following virtual host configuration. This example proxies all traffic to a backend application running on port 8080:

<VirtualHost *:80>
    ServerName yourdomain.com
    ServerAlias www.yourdomain.com

    # Disable forward proxying — this server is a reverse proxy only
    ProxyRequests Off

    # Preserve the original Host header sent by the client
    ProxyPreserveHost On

    # Forward all requests to the backend
    ProxyPass        / http://127.0.0.1:8080/
    ProxyPassReverse / http://127.0.0.1:8080/

    # Forward client IP address to the backend application
    RequestHeader set X-Real-IP       "%{REMOTE_ADDR}s"
    RequestHeader set X-Forwarded-For "%{REMOTE_ADDR}s"
    RequestHeader set X-Forwarded-Proto "http"

    ErrorLog  /var/log/httpd/myapp_error.log
    CustomLog /var/log/httpd/myapp_access.log combined
</VirtualHost>

Step 4: Understand ProxyPass and ProxyPassReverse

ProxyPass maps a URL path on Apache to a URL on the backend. In the example above, a request to /page on Apache is forwarded as a request to http://127.0.0.1:8080/page.

ProxyPassReverse rewrites Location, Content-Location, and URI response headers that the backend sends back. Without this directive, if your backend responds with a redirect to http://127.0.0.1:8080/login, the client receives that internal address instead of the public domain.

To proxy only a sub-path (e.g., /api/) to the backend while serving everything else from Apache’s document root:

ProxyPass        /api/ http://127.0.0.1:8080/api/
ProxyPassReverse /api/ http://127.0.0.1:8080/api/

The trailing slash is significant — always include it on both sides to avoid path duplication.

Step 5: Configure ProxyPreserveHost and Header Forwarding

ProxyPreserveHost On passes the original Host header from the client to the backend unchanged. This is essential when your backend application uses virtual hosting or constructs absolute URLs using the Host header.

When multiple clients pass through the proxy, each backend request originates from 127.0.0.1. Use RequestHeader (from mod_headers) to pass the real client IP:

RequestHeader set X-Real-IP       "%{REMOTE_ADDR}s"
RequestHeader set X-Forwarded-For "%{REMOTE_ADDR}s"
RequestHeader set X-Forwarded-Proto "http"

For HTTPS virtual hosts, change X-Forwarded-Proto to https so that your backend application can correctly generate secure links and redirect URIs.

Step 6: Configure SELinux to Allow Apache Network Connections

On RHEL 7, SELinux is enforcing by default. Apache is not permitted to initiate outbound network connections unless you explicitly grant it permission. Without this, you will see errors like:

(13)Permission denied: AH00957: HTTP: attempt to connect to 127.0.0.1:8080 (127.0.0.1) failed

Enable the httpd_can_network_connect SELinux boolean:

# Enable permanently
sudo setsebool -P httpd_can_network_connect 1

# Confirm the setting
getsebool httpd_can_network_connect
# httpd_can_network_connect --> on

If your backend is a database accessed directly by Apache (e.g., MySQL on 3306), also enable:

sudo setsebool -P httpd_can_network_connect_db 1

To use the more targeted approach of labeling only the specific port rather than opening all connections:

sudo yum install -y policycoreutils-python
sudo semanage port -a -t http_port_t -p tcp 8080
sudo semanage port -l | grep http_port_t

Step 7: Test the Configuration Syntax

Always validate before reloading:

sudo apachectl configtest

You must see Syntax OK. Common errors include a missing trailing slash in ProxyPass directives or a missing mod_headers load directive. After a clean test, reload Apache:

sudo systemctl reload httpd

Step 8: Test the Proxy

Send a request and verify the response comes from the backend:

curl -v http://yourdomain.com/

Check the backend application’s access log to confirm it is receiving requests with the correct headers. You can also use curl to inspect the headers Apache is sending to the backend by temporarily adding a header-echo endpoint to your backend, or by using a simple netcat listener:

# Listen on port 8080 and print all incoming data
nc -l 8080

Then send a request through Apache and observe the full HTTP request including all forwarded headers in the netcat output.

Step 9: Tune Connection Pooling (Optional)

Apache 2.4 on RHEL 7 supports connection pooling to backends via the enablereuse parameter in ProxyPass:

ProxyPass / http://127.0.0.1:8080/ 
    connectiontimeout=5 
    timeout=30 
    retry=1 
    max=100 
    ttl=120

ProxyPassReverse / http://127.0.0.1:8080/
  • connectiontimeout: seconds to wait for a connection to the backend
  • timeout: seconds to wait for the backend to respond
  • retry: seconds before retrying a backend that was previously unreachable (0 = always retry)
  • max: maximum number of pooled connections per child process
  • ttl: time-to-live for idle pooled connections

Apache’s mod_proxy provides a mature, production-tested reverse proxy that integrates naturally into any existing Apache deployment on RHEL 7. By enabling mod_proxy and mod_proxy_http in /etc/httpd/conf.modules.d/, writing clean ProxyPass and ProxyPassReverse directives, forwarding client headers with mod_headers, and applying the httpd_can_network_connect SELinux boolean, you can have a fully functional reverse proxy running alongside your existing Apache configuration in under fifteen minutes. This architecture is ideal for teams that already rely on Apache for other sites and want to avoid the complexity of running two web servers on the same host.