← All posts

Tutorial DevOps

How to Configure Apache to Serve a PHP App Behind a Reverse Proxy

Set up Apache as a reverse proxy for a PHP application with proper header forwarding and SSL termination support.

Difficulty Intermediate Time 30 min
Apache reverse proxy forwarding client requests to backend PHP application server with SSL termination

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.