“Knowledge isn't power until it is applied.”

— Dale Carnegie

Prologue

“nginx [engine x] is an HTTP and reverse proxy server, a mail proxy server, and a generic TCP/UDP proxy server, originally written by Igor Sysoev.”

- Nginx

So in this article, I’m going to talk about the basic use of Nginx proxying, including:

All these are done on a Ubuntu server. 😉

If you are familiar with these basic knowledge, you may want to check out Tips on Nginx Configuration first.😉


Setup Nginx

Before we use it, let’s have a brief look at Nginx.

First, we can get Nginx by a simple apt command.

1
sudo apt install nginx

If you accidentally corrupted your Nginx configuration, you can use apt purge nginx to remove it completely, and then reinstall it.

1
2
3
sudo apt purge nginx nginx-common nginx-full
sudo apt install nginx
nginx -t

Then, we’ll write some basic configurations in /etc/nginx/conf.d/default.conf. It tells Nginx to listen to port 80 requests and the server name it works on.

1
2
3
4
server {
listen 80;
server_name xx.xx.xx.xx;
}

If you have multiple profiles, you can then write different .conf under /etc/nginx/conf.d/, and Nginx will load them all.

To launch Nginx, you need these two commands, once and for all, unless you stop it permanently.

1
2
sudo nginx
systemctl start nginx.service

Every time you make any change to .conf files, you may want to reload Nginx, here is how to to it.

1
sudo nginx -s reload

And at last, if you want to stop Nginx, you can force terminate it immediately, or let it stop itself after all current works finished.

1
2
sudo nginx -s quit # stop after current process finishes its work
sudo nginx -s stop # stop immediately

Serving Static Files

Now, let’s begin with the easiest part - serving static files.

In our Web service, we may want our resources accessible via URL. For example, in Django project, the static/ and media/ folder will be exposed directly to URL when DEBUG set to True. However, Django won’t serve static files in production, when Nginx is needed.

In order to map URL to server directory, we can add location sections in server. For example, if we want to map /media route, we can add a location section called /media/. This tells Nginx to handle all URL routes begin with /media/ using schema defined in this section.

And in location section, we need to assign a root directory to tell Nginx to look for /media/ folder under this root. This way, Nginx will look for local file /home/wwwroot/media/avatar/1.png when receives URL /media/avatar/1.png.

1
2
3
4
5
6
7
8
server {
listen 80;
server_name xx.xx.xx.xx;

location /media/ {
root /home/wwwroot/;
}
}

However, if you don’t want the location appended to root, i.e. you want have /home/wwwroot/avatar/1.png when receives URL /media/avatar/1.png, you can use alias instead of root. So your configuration will be like this.

1
2
3
4
5
6
7
8
server {
listen 80;
server_name xx.xx.xx.xx;

location /media/ {
alias /home/wwwroot/;
}
}

If we have several static file routes, we can write root outside location to make it visible for all location sections, if it is not explicitly declared within.

1
2
3
4
5
6
7
8
9
10
11
12
server {
listen 80;
server_name xx.xx.xx.xx;

root /home/wwwroot/;

location /media/ {
}

location /static/ {
}
}

Basic Proxying

It is really convenient to use Nginx for backend API proxying, as it could hide annoying port suffix.

Proxying HTTP Requests

Basically, if we want to proxy our API for port 5000, we can simply write this. The only thing to notice is that, in order not to confuse API routes with others, such as static files, we can add a simple /api/ prefix to all our APIs.

1
2
3
4
5
6
7
8
9
server {
listen 80;
server_name xx.xx.xx.xx;

location /api/ {
proxy_pass http://localhost:5000/api/;
}
}

In this way, we can use xx.xx.xx.xx/api/your_api_route instead of xx.xx.xx.xx:5000/api/your_api_route.

Proxying WebSocket Requests

Reference: WebSocket Proxying

WebSocket is a way you can use to exchange data between client side and server side, regardless of whether there is a new request or not. In fact, it is a GET request, but then get a protocol upgrade when connection established. Then how to proxy for it?

Almost the same as HTTP requests, but with some more information. Also, you may add a /ws/ prefix, or whatever you like to avoid confusion.

1
2
3
4
5
6
7
8
9
10
11
12
server {
listen 80;
server_name xx.xx.xx.xx;

location /ws/ {
proxy_pass http://localhost:5000/ws/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 360s;
}
}

Here, we set the http version and headers to upgrade GET requests to a WebSocket connection. According to Nginx official site, the default timeout for a WebSocket connection is 60 seconds, which might be a little too short for your project. You can manually set it to any value you like. 😋

For WebSocket, instead of a long timeout, sending heart beat might be a better option.


Troubleshoot

403 Forbidden

Sometimes you may encounter 403 Forbidden when serving static files, don’t panic. It is most likely related to some permission issue. So let’s check it out.

First, let’s see who is running Nginx. We can use ps aux | grep nginx to find relevant processes. We can see that it is www-data, which is, obviously not us.

image-20240605230958244

So we need to change the owner of the Nginx worker process. First open /etc/nginx/nginx.conf (need sudo), and you’ll see a user option on top of the file. Change the default www-data to your username.

1
2
3
4
# user www-data;
user your-user-name;
worker_processes auto;
...

You need to make this user the same as the owner of the files you want to serve. Usually, it is you.

It must be the root configuration file nginx.conf, not any custom ones.

After this, use nginx -s reload to reload the nginx. And here we go, no more 403 Forbidden! 🎉


Epilogue

Well, this is merely the basic use of Nginx proxying. Explore more on your own. 🙂ᓚᘏᗢ