Using a Proxy Server to Host Multiple Domains

Apache is a great way to accomplish this.

After looking up many how-to’s to discover how to host multiple websites, each on their own domain name, and on their own server, but with only one available static IP address, I became frustrated because it seemed that everyone had their own answer to their own needs, but no one seemed to have the answer for what I needed specifically So I thought it would be good to post my solution for what I needed, with the simplest configuration to make it work, although maybe not best practice, but this simple solution will get you going, and you can add all of the optional stuff later.

If you’re reading this and need to work this out, here are the specifics of my needs:

  1. All servers behind a NAT on an inside address (eg. 192.168.1.X)
  2. Each web server running its own instance of WordPress.
  3. Every domain encrypted with SSL certificate, and the encryption handled centrally by the proxy server.
  4. All calls to port 80 or 443 from the outside, handled by one host acting as a proxy for all the domains, but the content is hosted by the individual boxes with their individual WordPress instance.

Here’s a simple block diagram of what I needed to do:

I’m using abc.com fo the example, and of course, you plug in the correct code for your IP address and domain name.
So a request for abc.com’s website would come into the modem on the outside IP address, it gets forwarded to the router, the router is programmed to receive all web requests and forward them to the proxy server, 192.168.1.5. The proxy server looks at the request packet and sees that abc.com should get this. But first it sees that it should be processing SSL for that traffic. The proxy server sends a normal http request (even though the browser sent it https) to the abc.com box as abc.com has not been configured for SSL. The abc box receives it on port 80 un-encrypted, and sends any resulting traffic back to the proxy server (seeing the proxy server as a web client, it doesn’t know the difference) still un-encrypted. The proxy server then encrypts it and sends it back through the router and modem to the waiting web surfer on the other side of his browser. That’s the over-all picture of how my needs are.

The first thing you need to do is designate a proxy server. A raspberry Pi works great for this. This will need to have a working instance of Certbot, and a few other bits of joy to make the proxy work. (I will use the term proxy for both forward and reverse proxy setup which is what this is. I will differientiate when needed.)

I always type “sudo su” to start my command prompt sessions to get root privileges. It makes it easier.

This will be the box you use for the proxy:
From a command line:

sudo su
apt install apache2 -y
a2emod proxy && a2enmod proxy_http && a2enmod ssl
a2dissite 000-default.conf
apt install certbot -y
apt install python-3-certbot-apache
systemctl restart apache2

So let’s walk through the simplest working example of this. Let’s assume we will be configuring abc.com. Let’s also assume you have a working website, abc.com on its own box on your network. If you type http://192.168.1.10 from a browser, you get a working web page from the box hosting abc.com website. We will also assume you own abc.com domain, and as of now, you have configured your registrar to send requests to abc.com to your public Ip address. The real big assumption is that you’ve already received from Let’s Encrypt, a SSL certificate for your site. It’s much easier to obtain it normally from a typical web server than from a proxy. You may refer back to my previous write-up about obtaining a free certificate. Here’s the rub… For a Proxy server, since the web content is on a different box than your SSL engine, Certbot won’t work because it can’t know that the certificate won’t be hosted on the content server. If I need to generate the certificate using Certbot from the proxy server, I configure a “hello world” simple abc.com.conf file to expose the site to the outside so as to satisfy Certbot. Then set up the proxy configuration below. Once the certificate is properly in /etc/letsencrypt/live/abc.com, Apache won’t cough up a hairball when it comes time to restart it.

Here are the steps to set up the proxy:

nano /etc/apache2/sites-available/abc.com.conf
This will open a new blank file. Populate the file with the following code:

<VirtualHost *:80>

       ServerName abc.com
       ServerAlias www.abc.com
       Redirect permanent / https://abc.com/

   </VirtualHost>

   <VirtualHost *:443>

       ServerName abc.com
       ServerAlias www.abc.com
       Redirect Permanent /www.abc.com/ /abc.com/

       RequestHeader set X-Forwarded-Proto "https"
       RequestHeader set X-Forwarded-Port "443"

    <Proxy *>
       Order deny,allow
       Allow from all
    </Proxy>
   
       ProxyPreserveHost On
       ProxyPass / http://192.168.1.10:80/
       ProxyPassReverse / http://192.168.1.10:80/

       SSLProxyEngine on
       SSLEngine on
       Include /etc/letsencrypt/options-ssl-apache.conf
       SSLCertificateFile /etc/letsencrypt/live/abc.com/fullchain.pem
       SSLCertificateKeyFile /etc/letsencrypt/live/abc.com/privkey.pem

   </VirtualHost>

The beauty of this set up is that no changes or configuration needs to be done on the content server side.
As far as it knows, there is no ssl, no proxy set up, just basic web services.
If you’re running WordPress, and you set it up through my script, it will just work. You will need to make sure to set this in the configuration:

If you didn’t use my script, insert this code into your wp-config.php file in the WP root directory:
The red code is what you insert between the other 2 lines.

/* Add any custom values between this line and the “stop editing” line. */

if (!empty($_SERVER[‘HTTP_X_FORWARDED_PROTO’]) && $_SERVER[‘HTTP_X_FORWARDED_PROTO’] === ‘https’) {$_SERVER[‘HTTPS’] = ‘on’;}

/* That’s all, stop editing! Happy publishing. */

That should be it. Hopefully I’ve included everything, but please let me know in the comments if I’ve missed something.