A reverse proxy sits in front of your application servers and forwards client requests to the backend. In this tutorial, I will configure Apache as a reverse proxy for a PHP application running on a different port or server. This setup is common for load balancing, SSL termination, or isolating application servers from direct internet access. By the end, Apache will forward requests to your backend PHP app and return responses to clients.
Prerequisites
- A LAMP server with Apache 2.4 or later installed
- Root or sudo access
- A PHP application running on a backend server or different port
- Basic familiarity with Apache virtual hosts
Step 1 - Enable Required Apache Modules
Apache needs the proxy modules to forward requests. Enable them with the a2enmod command on Ubuntu and Debian:
sudo a2enmod proxy sudo a2enmod proxy_http sudo a2enmod proxy_balancer sudo a2enmod lbmethod_byrequests sudo a2enmod headers
On CentOS or RHEL, these modules are usually enabled by default. Verify they are loaded by checking the main Apache configuration file.
Restart Apache to load the modules:
sudo systemctl restart apache2
On CentOS or RHEL:
sudo systemctl restart httpd
Step 2 - Identify Your Backend Application
Determine where your PHP application is running. Common scenarios include:
- A PHP-FPM or built-in server on localhost port 8000
- An application server on another machine at 192.168.1.10:8080
- A containerized app on 127.0.0.1:3000
For this tutorial, I assume a PHP application is running on localhost port 8000. If your backend is elsewhere, adjust the proxy target accordingly.
Test that your backend is responding:
curl http://localhost:8000
You should see HTML output from your application. If the connection is refused, start your backend application first.
Step 3 - Create a Virtual Host for the Reverse Proxy
Create a new Apache virtual host configuration file. On Ubuntu and Debian:
sudo nano /etc/apache2/sites-available/myapp.conf
On CentOS or RHEL:
sudo nano /etc/httpd/conf.d/myapp.conf
Add this basic reverse proxy configuration:
<VirtualHost *:80> ServerName myapp.example.com ServerAdmin webmaster@example.com
text
ProxyPreserveHost On
ProxyPass / http://localhost:8000/
ProxyPassReverse / http://localhost:8000/
ErrorLog ${APACHE_LOG_DIR}/myapp_error.log
CustomLog ${APACHE_LOG_DIR}/myapp_access.log combined
Replace myapp.example.com with your actual domain. The ProxyPass directive forwards all requests to the backend. ProxyPassReverse rewrites response headers so redirects work correctly.
Save and close the file.
Step 4 - Enable the Virtual Host
On Ubuntu and Debian, enable the new site:
sudo a2ensite myapp.conf
Test the Apache configuration for syntax errors:
sudo apache2ctl configtest
You should see Syntax OK. If errors appear, review your configuration.
Reload Apache to apply the changes:
sudo systemctl reload apache2
On CentOS or RHEL:
sudo systemctl reload httpd
Step 5 - Configure Proxy Headers for Client Information
By default, your backend sees all requests coming from the proxy server, not the original client. Forward client IP and protocol information with additional headers. Edit your virtual host configuration:
sudo nano /etc/apache2/sites-available/myapp.conf
Add these RequestHeader directives inside the VirtualHost block:
<VirtualHost *:80> ServerName myapp.example.com ServerAdmin webmaster@example.com
text
ProxyPreserveHost On
RequestHeader set X-Forwarded-Proto "http"
RequestHeader set X-Forwarded-For "%{REMOTE_ADDR}s"
RequestHeader set X-Real-IP "%{REMOTE_ADDR}s"
ProxyPass / http://localhost:8000/
ProxyPassReverse / http://localhost:8000/
ErrorLog ${APACHE_LOG_DIR}/myapp_error.log
CustomLog ${APACHE_LOG_DIR}/myapp_access.log combined
These headers tell your backend application the original client IP address and protocol. Your PHP app can read them to get the real client information.
Reload Apache:
sudo systemctl reload apache2
Step 6 - Test the Reverse Proxy
Point your domain to the Apache server IP address using DNS or edit your local hosts file for testing. On Linux or macOS:
sudo nano /etc/hosts
Add a line:
127.0.0.1 myapp.example.com
Save the file. Now test the proxy with curl:
curl -I http://myapp.example.com
You should see HTTP headers from your backend application served through Apache. Visit the URL in a browser to confirm the full page loads.
If you see a 503 Service Unavailable error, the backend is not running or Apache cannot reach it. Check that your backend is listening on the correct port.
Step 7 - Add SSL Termination
For production, terminate SSL at the reverse proxy so the backend only handles HTTP. First, obtain an SSL certificate using Let's Encrypt or another provider. Install Certbot:
sudo apt install certbot python3-certbot-apache -y
Request a certificate:
sudo certbot --apache -d myapp.example.com
Certbot automatically updates your virtual host to handle HTTPS. After completion, your configuration will include a new VirtualHost on port 443 with SSL enabled.
Update the HTTPS virtual host to include the X-Forwarded-Proto header with value https:
sudo nano /etc/apache2/sites-available/myapp-le-ssl.conf
Find the RequestHeader line for X-Forwarded-Proto and change it to:
RequestHeader set X-Forwarded-Proto "https"
This tells your backend that the original request was over HTTPS, even though the proxy communicates with the backend over HTTP.
Reload Apache:
sudo systemctl reload apache2
Step 8 - Handle WebSocket Connections
If your PHP application uses WebSockets, you need additional configuration. Enable the WebSocket proxy module:
sudo a2enmod proxy_wstunnel
Add a conditional proxy for WebSocket upgrade requests in your virtual host:
RewriteEngine On RewriteCond %{HTTP:Upgrade} =websocket [NC] RewriteRule /(.*) ws://localhost:8000/$1 [P,L] RewriteCond %{HTTP:Upgrade} !=websocket [NC] RewriteRule /(.*) http://localhost:8000/$1 [P,L]
This checks if the request is a WebSocket upgrade and routes it to the ws:// protocol endpoint. Restart Apache after adding this configuration.
Step 9 - Monitor Proxy Performance
Check Apache access and error logs to monitor proxy activity:
sudo tail -f /var/log/apache2/myapp_access.log sudo tail -f /var/log/apache2/myapp_error.log
Look for HTTP status codes. A 502 Bad Gateway means the backend is unreachable. A 503 Service Unavailable means the backend is not responding in time.
To see real-time requests being proxied, watch the access log while visiting your site in a browser.
Step 10 - Secure the Backend
Since Apache now handles all public traffic, configure your backend to only accept connections from the proxy. If your backend is on the same server, bind it to localhost only so it cannot be accessed from the network.
For a PHP built-in server:
php -S 127.0.0.1:8000
If your backend is on another server, use firewall rules to only allow traffic from the proxy server IP address. This prevents clients from bypassing the proxy and accessing the backend directly.
Wrap-up
You have configured Apache as a reverse proxy for a PHP application. Apache now handles incoming requests, forwards them to the backend, and returns responses to clients. This setup allows you to add SSL termination, load balancing, caching, and security layers without modifying your application code. For production, always use HTTPS, forward client headers correctly, and restrict backend access to the proxy server only.