I am hosting both https://irtizahafiz.com and https://umami.irtizahafiz.com on my Linux server using Nginx reverse proxies.
The latter is running locally in a docker container while the former is a NextJS application.
These are only 2 of the 10 different web apps being served from one server. That’s the power of reverse proxies.
In this blog post, I will show you how you can do the same with Nginx reverse proxies. Let’s get started.
My portfolio website is a NextJS application running on my server’s port 3000.
Similarly, my web analytics app, Umami, is running inside a docker container on my server’s port 7001.
I can easily access both of these running processes from inside my server, or through a SSN tunnel between 2 machines. However, the fun begins when I try to expose them to the public internet.
Let’s take a step back and try to understand what I am trying to achieve here.
When someone visits https://irtizahafiz.com, they see my NextJS application.
When someone visits https://newsletter.irtizahafiz.com, they see my Umami instance
(Coming Up) When someone visits https://creditBuilder.irtizahafiz.com, they see my ReactJS application.
In this way, I can have all my different web apps running locally on my server, and whenever I want to expose them to the public internet, I can just connect them to a new subdomain.
To achieve the above setup, I need a “web server” that appropriately routes traffic coming to my machine’s port 80 (HTTP) or port 443 (HTTPS) to the relevant service.
The first step is to connect my root domain (irtizahafiz.com) to my server.
Wherever you purchase your domain from (GoDaddy, Namecheap, etc), they will give you a dashboard to do this. I use GoDaddy, where I can easily create a DNS record to link my domain to my server’s IP address.
Once that’s done, whenever a user types irtizahafiz.com in their browser, the request will make its way to your server’s port 80 or port 443.
HTTP Traffic — comes to port 80
HTTPS Traffic — comes to port 443
So, if a user types in http://irtizahafiz.com, your server gets a ping on port 80. If a user types in https://irtizahafiz.com, your server gets a ping on port 443.
On your web server, Nginx, you can easily set up a redirect, where traffic coming to port 80 will be redirected to port 443, hence making the connection between the browser and server more secure.
Okay, now that you understand a little about HTTP vs HTTPS, and hopefully you have your root domain connected to your server, we will move on to reverse proxies.
What does a reverse proxy do?
It proxy passes (or forwards) incoming web traffic to other machines or ports within the same machine. Let’s look at an example.
server {
listen 443 ssl;
server_name irtizahafiz.com;
location / {
proxy_pass http://127.0.0.1:3000;
}
ssl_certificate /etc/letsencrypt/live/irtizahafiz.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/irtizahafiz.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}
server {
listen 80;
server_name irtizahafiz.com;
# Redirect HTTP requests to HTTPS
if ($scheme != "https") {
return 301 https://$host$request_uri;
}
}
Here’s how Nginx is acting as a reverse proxy:
For HTTPS traffic, it’s forwarding the request to port 3000 (where my NextJS app is running)
For HTTP traffic, it’s redirecting the user to “HTTPS”, hence letting port 443 do the job (described in the above bullet point)
The key is that in both cases, whether the request is coming to http://irtizahafiz.com or https://irtizahafiz.com, it’s forwarding it to port 3000 where my NextJS app is running.
Hence, the user is seeing my NextJS app when they visit my domain.
Now that the root domain is working as expected, let’s make https://umami.irtizahafiz.com work similarly.
The first step will be to create another A record for the subdomain using your domain provider (in my case GoDaddy). It’s a simple one-line option that takes less than 2 minutes.
Once that’s done when someone types umami.irtizahafiz.com in their browser, the request will make its way to your server’s port 80 or port 443 (depending on HTTP or HTTPS).
On your server, you have to handle the “routing” where requests coming to this particular subdomain get routed to the docker container running Umami.
Let’s look at the next part of the Nginx config file.
server {
listen 443 ssl;
server_name umami.irtizahafiz.com;
location / {
proxy_pass http://127.0.0.1:7001;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
}
ssl_certificate /etc/letsencrypt/live/irtizahafiz.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/irtizahafiz.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}
server {
listen 80;
server_name umami.irtizahafiz.com;
# Redirect HTTP requests to HTTPS
if ($scheme != "https") {
return 301 https://$host$request_uri;
}
}
Again, here’s how the Nginx reverse proxy is working for a subdomain:
For HTTPS traffic to umami.irtizahafiz.com, the request is proxy passed (or forwarded) to port 7001 where the Umami docker instance is running.
For HTTP traffic, the request is redirected or “upgraded” to use HTTPS protocol (then the first bullet point executes).
I have only shown you 2 instances of Nginx reverse proxy allowing you to host multiple apps on the same machine, and exposing them to the public internet using subdomains.
I already have 6 more web apps running on my machine, some exposed to the internet, and others with restricted access.
The point is — you can host as many web apps or APIs as you want with subdomains and reverse proxies.
If you have made it this far, I hope you found this valuable.
Feel free to follow me on Medium, subscribe to my website, or follow me on YouTube.