This is likely to be a long one…

If you have various services on your network to the point you can’t remember all the different IP addresses and port numbers…a reverse proxy is for you. Ordinarily this is done so that you can make services public facing, but sometimes you don’t want to expose a service to the filth of the internet. You can set your reverse proxy to work internally!

There are three-ish steps to getting this working depending on your requirements.

For this guide I’ll assume your internal domain name will be *.localdomain. I think you can use Let’s Encrypt with a DNS challenge for this if you want to use a registered FQDN, but if you don’t want to use one this guide will show how.

1. Set DNS entry

For when you don’t need to specify a port

This is straightforward enough if you’re running your own DNS server (like PiHole etc). Just head to Local DNS → DNS Records and add the hostname you want (e.g. service.localdomain) and the internal IP address you want this to resolve to. Remember that your service should be static or your domain name mapping won’t last long.

Similarly, some routers also allow you to set custom local DNS entries - check the manual!

You may have to flush your DNS cache on your client machine to properly access the hosted service on its spanky new internal domain name.

2. Set reverse proxy entry

For when you need to specify a port, URL path or use https rewriting automatically

Often a service will run on a specific port or path, in which case DNS alone cannot pass on this information (although Step 1 is still necessary in all the other steps). …Enter a reverse proxy. I’ve been using Apache for years to do this, which probably makes me out of date and unfashionable. If you use NGINX, Traefik, Caddy etc…great! Go work out how to do this for yourself as I’ve no idea.

Proxying internal hosts is basically the same as external ones. It’s as easy as creating a new virtualhost for your internal domain names you want proxied to.

<VirtualHost *:80>
ServerName your_service.localdomain
ProxyPass / http://ip_of_service:port/ 
ProxyPassReverse / http://ip_of_service:port/
</VirtualHost>

Then enable the virtualhost and restart Apache. This is about as simple as it gets.

However life and tech is rarely this simple! What if you want to serve your internal services over https? A not unreasonable requirement in this day and age. The following will get you an encrypted connection (albeit with a self signed certificate) from your reverse proxy to your client machine (Read below for backend services that are served via HTTPS already).

First, ensure rewriting to https from the http connection is enabled (like in the example above):

<VirtualHost *:80>
ServerName your_service.localdomain
RewriteEngine on
RewriteCond %{SERVER_NAME} =your_service.localdomain
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>

Then create the SSL version of the VirtualHost:

<VirtualHost *:443>
ServerName soulseek.localdomain
ProxyPreserveHost On
ProxyPass / http://ip_of_service:port/
ProxyPassReverse / http://ip_of_service:port/
SSLCertificateFile /etc/ssl/certs/apache-selfsigned.crt
SSLCertificateKeyFile /etc/ssl/private/apache-selfsigned.key
</VirtualHost>

Enable the VirtualHost and restart Apache…and now your internal reverse proxy should be serving your backend service over https. It will be a self-signed certificate still, but it should work.

3. What about HTTPS backends?

For when the backend service is served over https

These days you really want to ensure that your services are using https rather than http for added security through encryption. However, if your backend service is being served over https, how can your reverse proxy accept the certificate (especially if it is self-signed?). Services like Proxmox (rightly) are only accesible via https, for example.

There are two options: - Import the backend services certificate into your reverse proxy - Generate a new certificates and put it on your reverse proxy and backend service

For option 1, you can grab the certificate of your backend service by viewing it in firefox. Download the Pem (Cert) linked file and put it on your reverse proxy. In may case, the location was /etc/ssl/certs and I ensure the certificate was accesible by root only. Add the following to your Apache VirtualHost:

SSLProxyVerify require
SSLProxyCheckPeerCN off
SSLProxyCheckPeerName off
SSLProxyEngine On
SSLEngine on
SSLProxyCACertificateFile /etc/ssl/certs/your-downloaded-service-certificate.pem

Save & restart Apache and your https serving backend should now be accesible through your internal reverse proxy.

Lock down who can access your reverse proxied backend service by ip range or specific address by adding the following to your Apache VirtualHost;

<Location "/">
    Require ip 192.168.1.0/24 (IP range or individual address)
    Require all denied
</Location>

Additionally, the SSLProxyCheckPeerCN off and SSLProxyCheckPeerName off directives are kind of not great and can leave you potentially open to Man-In-The-Middle attacks. Now, given this reverse proxy is internal, it is locked by connecting ip and you can just go directly to the service without using the proxy anyway, perhaps this isn’t a big deal. If you still think it is a big deal, you might want to generate a certificate for the backend service and proxy. Read on…

4. Security considerations and generating your own certificate

I have a service that seems to regenerate a random certificate everytime it starts- I decided to set it to a permanent certificate, rather than a random new one each restart. The upside of this is that I can ensure the certificate matches the internal domain name when I generate it, so can turn the SSLProxyCheckPeerCN and SSLProxyCheckPeerName directives on. The downside is that it is more steps and you have to replace the certificates manually when they run out (better set your calendar or something).


To create certificates without any prior key files:

If you don’t have any initial files (like a private key or certificate), you can generate them from scratch. Here’s how to create both a self-signed certificate and a private key, and then export them to both PEM and PFX formats using OpenSSL.

Step 1: Generate a Private Key

Run the following command to create a new private key:

openssl genpkey -algorithm RSA -out privatekey.pem -pkeyopt rsa_keygen_bits:2048

Step 2: Create a Self-Signed Certificate

Next, use the private key to create a self-signed certificate:

openssl req -new -x509 -key privatekey.pem -out certificate.pem -days 365

You will be prompted to enter information for the certificate, such as country, state, and organization. The common name should be your-service.localdomain

Step 3: Create a PFX File

Now you can create a PFX file from the private key and certificate:

openssl pkcs12 -export -out output.pfx -inkey privatekey.pem -in certificate.pem

If you want to include a password for the PFX file, you will be prompted to enter one during the export process. Move the pfx file to the backend service and configure it use it (you’re on your own here, check the documentation) and the certificate file should be moved to the reverse proxy (as mentioned previously when using https backends).


Ok, so this was a massive amount of stuff which really only gives an outline, but hopefully someone will find it useful. Inevitebly I will forget how I did it last time and revisit this myself.