To learn what a reverse proxy is and why you should use one, read the explanation here.
There are some prerequisites you’ll need before setting up a reverse proxy server. The first thing you’ll need is to have port 80 and 443 of your public IP address forwarded to the machine you want to use as a proxy. This can be configured through your router’s admin page. You will also need a domain name with an A record that points to your public IP. Finally, you’ll need some services running on your local network for you to proxy.
First, type sudo apt update
to update the package information.
Then, type sudo apt install nginx
to install nginx.
Finally, allow the necessary ports using sudo ufw allow 80/tcp
and sudo ufw allow 443/tcp
.
First, enable the EPEL repository using sudo yum install epel-release
.
Then, type sudo yum install nginx
to install nginx.
Finally, allow the necessary ports using sudo firewall-cmd --permanent --zone=public --add-service=http
and sudo firewall-cmd --permanent --zone=public --add-service=https
. Also, type sudo firewall-cmd --reload
to reload the firewall.
Make sure nginx starts up using sudo systemctl start nginx
.
To verify that nginx is working properly, visit http://yoursite.com
and you should see a nginx welcome page similar to what’s shown below. This specific page may vary depending on your distro.
Before we actually create our reverse proxy configuration, we have to decide which local servers will handle each of the subdomains. For example, if I wanted nextcloud.yoursite.com
to be handled by a server at 192.168.0.230
, I could add a nginx configuration for that.
Once you’ve decided which subdomains you’ll use, add DNS CNAME records that map the subdomain to your main domain name. Below is an example in Google Domains, but it will vary depending on your DNS provider.
Note: Editable templates for each of the config files shown in this guide can be found at this GitHub repo.
In order to set up the reverse proxy, we have to remove the default website and add our own configuration to handle each subdomain. In this guide, we’ll create two config files, one for a www/non-www domain and one for any other subdomain.
To remove the default configuration, we can type cd /etc/nginx/sites-enabled/
to enter the directory and sudo rm default
to remove the config file.
To begin, type cd /etc/nginx/sites-available/
to enter the sites-available directory. Then type sudo vi reverse-proxy.conf
to begin editing the file.
The first thing you’ll want to add in this file is a server block. This server block will listen on http://www.yoursite.com
and redirect visitors to https://www.yoursite.com
.
server {
listen 80;
server_name www.yoursite.com;
return 301 https://www.yoursite.com$request_uri;
}
The next thing to add is another server block, which will listen on http://yoursite.com
and redirect visitors to https://www.yoursite.com
server {
listen 80;
server_name yoursite.com;
return 301 https://www.yoursite.com$request_uri;
}
Our third server block will listen on https://yoursite.com
, and redirect the https traffic to https://www.yoursite.com
. This server block also contains information about the SSL certificates, which we will modify later when we obtain them.
server {
listen 443;
server_name yoursite.com;
return 301 https://www.yoursite.com$request_uri;
# SSL Configuration
ssl_certificate /etc/letsencrypt/live/yoursite.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/yoursite.com/privkey.pem; # managed by Certbot
ssl on;
ssl_session_cache builtin:1000 shared:SSL:10m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;
ssl_prefer_server_ciphers on;
}
This last server block will perform the actual proxying. It will listen on https://www.yoursite.com
and proxy requests to a backend server. To do this, we can add a location block within this server block. Within the location block, we set proxy headers which nginx forwards to the backend, and we add the proxy pass and proxy redirect with the IP address and port of the backend server. The last few lines are optional, but I recommend using them because they heighten the security of your server. These lines enable HSTS, clickjacking protection, XSS protection, and disable content and MIME sniffing. Finally, we can add a line which adds the trailing slash to all URLs.
server {
listen 443;
server_name www.yoursite.com;
# SSL Configuration
ssl_certificate /etc/letsencrypt/live/yoursite.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/yoursite.com/privkey.pem; # managed by Certbot
ssl on;
ssl_session_cache builtin:1000 shared:SSL:10m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;
ssl_prefer_server_ciphers on;
# Set the access log location
access_log /var/log/nginx/yoursite.access.log;
location / {
# Set the proxy headers
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Configure which address the request is proxied to
proxy_pass http://yourserverip:yourport/;
proxy_read_timeout 90;
proxy_redirect http://yourserverip:yourport https://www.yoursite.com;
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header Referrer-Policy "origin";
# Add the trailing slash
rewrite ^([^.]*[^/])$ $1/ permanent;
}
}
After adding these lines, type :wqa
to save the file and exit Vim.
There’s one more step before we can use this config: symlinking it to the sites-enabled directory (which nginx reads). To do this, type sudo ln -s /etc/nginx/sites-available/reverse-proxy.conf /etc/nginx/sites-enabled/reverse-proxy.conf
.
This next config file will serve as a template for any other subdomains you want to add to your reverse proxy. To begin making this config file, type cd /etc/nginx/sites-available/
and then sudo vi SUBDOMAIN.conf
, replacing “SUBDOMAIN” with the subdomain you want to configure.
The first thing we’ll add in this file is a server block. This server block will listen on http://YOURSUBDOMAIN.YOURSITEDOMAIN.com
and redirect visitors to https://YOURSUBDOMAIN.YOURSITEDOMAIN.com
.
server {
listen 80;
server_name YOURSUBDOMAIN.YOURSITEDOMAIN.com;
return 301 https://$host$request_uri;
}
This next server block will perform the actual proxying. It will listen on https://YOURSUBDOMAIN.YOURSITEDOMAIN.com
and proxy requests to your backend server. To do this, we’ll add a location block inside the server block. Within the location block, we set proxy headers which nginx forwards to the backend, and we add the proxy pass and proxy redirect with the IP address and port of the backend server. Again, the security headers at the bottom are optional, but they will greatly improve the security of your server, so I recommend that you add them.
server {
listen 443;
server_name YOURSUBDOMAIN.YOURSITEDOMAIN.com;
# SSL configuration
ssl_certificate /etc/letsencrypt/live/YOURSUBDOMAIN.YOURSITEDOMAIN.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/YOURSUBDOMAIN.YOURSITEDOMAIN.com/privkey.pem; # managed by Certbot
ssl on;
ssl_session_cache builtin:1000 shared:SSL:10m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;
ssl_prefer_server_ciphers on;
# Set the access log location
access_log /var/log/nginx/YOURSUBDOMAIN.access.log;
location / {
# Set the proxy headers
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Configure which address the request is proxied to
proxy_pass http://YOURSERVER:YOURPORT;
proxy_read_timeout 90;
proxy_redirect http://YOURSERVER:YOURPORT https://YOURSUBDOMAIN.YOURSITEDOMAIN.com;
# Set the security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"; #HSTS
add_header X-Frame-Options DENY; #Prevents clickjacking
add_header X-Content-Type-Options nosniff; #Prevents MIME sniffing
add_header X-XSS-Protection "1; mode=block"; #Prevents cross-site scripting attacks
add_header Referrer-Policy "origin";
}
}
After adding these lines, type :wqa
to save the file and exit Vim.
Finally, to symlink this file to the sites-enabled directory, type sudo ln -s /etc/nginx/sites-available/SUBDOMAIN.conf /etc/nginx/sites-enabled/SUBDOMAIN.conf
.
To add any additional subdomains, simply copy the previous config file and replace the server_name with the new subdomain, along with the backend’s IP address and port. Then symlink the new file to the sites-enabled directory.
If you try to restart nginx at this stage (sudo systemctl restart nginx
), you’ll probably see a few errors saying that the certificate files don’t exist. In order to get nginx to start, we’ll have to use a temporary certificate.
To obtain a temporary certificate and store it in the working directory, type openssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out certificate.pem
. Two files, key.pem
and certificate.pem
will be stored in your working directory.
Now modify these two lines in your config files
ssl_certificate /etc/letsencrypt/live/yoursite.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/yoursite.com/privkey.pem; # managed by Certbot
so that they look like this:
ssl_certificate /path/to/certificate.pem; # managed by Certbot
ssl_certificate_key /path/to/key.pem; # managed by Certbot
Make sure to replace /path/to/
with the path to your certificate and key files.
After modifying these lines in each config, we can restart nginx using sudo systemctl restart nginx
.
Now that nginx has restarted with the new configuration, we can obtain SSL certificates from Let’s Encrypt, a certificate authority that provides free certificates. To obtain a Let’s Encrypt certificate, we can use Certbot.
To install Certbot on a Debian-based distro, type sudo apt install python3-certbot-nginx
.
To install on a RHEL-based distro, type sudo yum install certbot python3-certbot-nginx
.
Then, to obtain certificates for your www and non-www domains, type sudo certbot --nginx -d YOURSITEDOMAIN.com -d www.YOURSITEDOMAIN.com
.
Certbot will ask for some information, including your email address, agreement to the Terms of Service, and whether or not you want to subscribe to their newsletter. Then, Certbot will obtain your certificate.
To obtain a certificate for any additional subdomains, type sudo certbot --nginx -d sub.domain.com
, replacing sub.domain.com
with the proper subdomain address.
Certbot will automatically update the config files with the path to your new certificates, so you don’t need to do that manually.
Once you’ve obtained all the certificates you need, restart nginx with sudo systemctl restart nginx
.
Now, visit each of your subdomains and ensure that they are accessible over https.
The last thing we should do is to set up the auto-renewal of SSL certificates using cron. To do this, open the crontab for editing by typing sudo crontab -e
.
Then add the following line to the crontab to automatically try to renew the certificates at 1:00am every day:
0 1 * * * certbot renew --deploy-hook "systemctl restart nginx"
In conclusion, a reverse proxy allows you to easily host multiple sites on the same IP address without exposing unnecessary ports. If you enjoyed this article, feel free to check out my website, where I post articles about upgrading/restoring computers, securing your servers, and more. Thanks for reading and happy self-hosting!