Setting Up Apache2 as a Reverse Proxy

Apache can act as a reverse proxy, forwarding requests to a backend application server (Node.js, a Python app, another internal service) while presenting a unified public-facing domain and handling SSL termination.

<VirtualHost *:443>
    ServerName app.example.com

    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/app.example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/app.example.com/privkey.pem

    ProxyPreserveHost On
    ProxyPass / http://127.0.0.1:3000/
    ProxyPassReverse / http://127.0.0.1:3000/

    # Needed for WebSocket support (e.g. a Node.js app using Socket.IO)
    RewriteEngine On
    RewriteCond %{HTTP:Upgrade} =websocket [NC]
    RewriteRule /(.*) ws://127.0.0.1:3000/$1 [P,L]
</VirtualHost>
sudo a2enmod proxy proxy_http proxy_wstunnel rewrite
sudo systemctl restart apache2

ProxyPreserveHost On matters more than it looks — without it, the backend application sees Apache’s internal hostname instead of the real public domain, which breaks anything relying on the Host header (redirects, generated URLs, some auth logic).