Introduction
A reverse proxy is the recommended method for exposing an application server to the Internet. Whether you’re running a Node.js an application in production, or a minimal integrated web server with Flask, these application servers will often bind to localhost with a TCP port. This means that, by default, your application will only be accessible locally on the machine where it resides. Although you can specify a different endpoint to force access over the Internet, these application servers are designed to be served from behind a reverse proxy in production environments. This provides security benefits by isolating the application server from direct Internet access, the ability to centralize firewall protection, and a minimized attack plane for common threats such as denial-of-service attacks.
From a customer’s perspective, interacting with a reverse proxy is no different from interacting directly with the application server. It’s functionally the same, and the customer can’t tell the difference. A client requests a resource and then receives it, without any additional configuration required by the client.
This tutorial will demonstrate how to set up a reverse proxy using Nginx, a popular web server and reverse proxy solution. You’ll install Nginx, configure it as a reverse proxy using the proxy_pass policy, and forward the appropriate headers from your client’s request. If you do not have an application server handy to test, you will optionally configure a test application with the WSGI Gunicorn server.
Prerequisites To
complete this tutorial,
you will need:
An Ubuntu 22.04 server,
- configured according to our initial server setup guide for Ubuntu 22.04
- The address of the application server you want to proxy, this will be referred to app_server_address throughout the tutorial. This can be an IP address with a TCP port (such as the Gunicorn default of http://127.0.0.1:8000) or a Unix domain socket (such as http://unix:/tmp/pgadmin4.sock for pgAdmin). If you don’t have an application server set up to test, you’ll be guided through setting up a Gunicorn app that will link to http://127.0.0.1:8000.
- A domain name pointing to your server’s public IP. This will be configured with Nginx to proxy your application server.
,
Step 1 – Nginx Installation
Nginx is available for installation with apt through the default repositories. Update the repository index, and then install Nginx:
sudo apt
- update sudo apt
- install nginx
Press Y to confirm the installation. If you are prompted to restart the services, press ENTER to accept the default values.
You must allow access to Nginx through your firewall. After configuring your server according to the initial server prerequisites, add the following rule with
ufw:
- sudo ufw allow
‘Nginx HTTP’
You can now verify that Nginx is running
:
- systemctl status
nginx Output● nginx.service – A high-performance web server and reverse proxy server Loaded: loaded (/lib/systemd/system/nginx.service; enabled; provider preset: enabled) Active: active (running) since Mon 2022-08-29 06:52:46 UTC; 39min ago Documents: man:nginx(8) Main PID: 9919 (nginx) Tasks: 2 (limit: 2327) Memory: 2.9M CPU: 50ms CGroup: /system.slice/nginx.service ├─9919 “nginx: master process /usr/sbin/nginx -g daemon on; master_process on;” └─9920 “nginx: worker process
Next, you’ll add a custom server block with your domain and application server proxy.
Step 2 — Server Block Configuration
It is recommended that you create a custom configuration file for new server block additions, rather than editing the default settings directly. Create and open a new Nginx configuration file using nano or your preferred text editor:
- sudo nano /etc/nginx/sites-available/
your_domain
Insert the following into your new file, making sure to replace your_domain and app_server_address. If you do not have an application server to test with, by default use http://127.0.0.1:8000 for the optional Gunicorn server configuration in Step 3: server
{ listen 80; listen [::]:80; server_name your_domain www.your_domain; location / { proxy_pass app_server_address; include proxy_params; } }
Save and exit, with nano you can do this by pressing CTRL+O and then CTRL+X.
This configuration file starts with a standard Nginx configuration, where Nginx will listen on port 80 and respond to requests made to your_domain and www.your_domain. Reverse proxy functionality is enabled through Nginx’s proxy_pass policy. With this setting, navigating to your_domain in your local web browser will be the same as opening app_server_address on your remote machine. While this tutorial will only send one proxy to a single application server, Nginx is capable of serving as a proxy for multiple servers at once. By adding more location blocks as needed, a single server name can combine multiple application servers through proxy into a cohesive web application.
All HTTP requests come with headers, which contain information about the client that sent the request. This includes details such as IP address, cache preferences, cookie tracking, authorization status, and more. Nginx provides some recommended header forwarding settings that it has included as proxy_params, and details can be found in /etc/nginx/proxy_params:
proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header $scheme X-Forwarded-Proto;
With reverse proxies, your goal is to convey relevant information about the client and sometimes information about your own reverse proxy server. There are use cases where a proxy server would want to know which reverse proxy server handled the request, but usually the important information comes from the original client request. To transmit these headers and make the information available in the places where it is expected, Nginx uses the proxy_set_header directive.
By default, when Nginx acts as a reverse proxy, it alters two headers, removes all empty headers, and then passes the request. The two modified headers are the Host header and the Connection header. There are many HTTP headers available, and you can check out this detailed list of HTTP headers to learn more about each of their purposes, though those relevant to reverse proxies will be covered here later.
Here are the headers forwarded by proxy_params and the variables in
which it stores the data:
- Host: This header contains the original host requested by the client, which is the domain and port of the website. Nginx keeps this in the $http_host variable.
- X-Forwarded-For: This header contains the IP address of the client that sent the original request. It can also contain a list of IP addresses, with the IP of the original client first, then a list of all the IP addresses of the reverse proxy servers that passed the request. Nginx keeps this in the variable $proxy_add_x_forwarded_for.
- X-Real-IP: This header always contains a single IP address that belongs to the remote client. This contrasts with the similar X-Forwarded-For which can contain an address list. If X-Forwarded-For is not present, it will be the same as X-Real-IP.
- X-Forwarded-Proto: This header contains the protocol used by the original client to connect, either HTTP or HTTPS. Nginx keeps this in the $scheme variable.
Next, enable this configuration file by
creating a link from it to the site-enabled directory that Nginx reads at startup
: sudo ln -s /etc/nginx/sites-available/your_domain /etc/nginx/
- sites-enabled/
You can now test your configuration file for syntax errors
: sudo nginx -t
With no reported issues, restart Nginx to apply your changes:
- sudo systemctl restart nginx Nginx
is
now configured as a reverse proxy for your application server, and you can access it from a local browser if your application server is running. If you have an application server planned but do not have it running, you can start the desired application server. You can skip the rest of this tutorial.
Otherwise, proceed to set up a test application and server with Gunicorn in the next step.
Step 3 — Testing Your Reverse Proxy
with Gunicorn (optional) If you had an application server
up and running before starting this tutorial, you can visit it in your browser now:
your_domain
However, if you don’t have an application server on hand to test your reverse proxy, you can follow the steps below to install Gunicorn along with a test application. Gunicorn is a Python WSGI server that is often paired with an Nginx reverse proxy.
Update your apt repository index and
install gunicorn: sudo apt
- update sudo
- gunicorn
apt install
You also have the option to install Gunicorn via pip with PyPI for the latest version that can be paired with a Python virtual environment, but apt is used here as a quick testbed
.
Next, you’ll write a Python function to return “Hello World!” as an HTTP response to be rendered in a web browser. Create test.py using nano or your preferred text editor
:
- nano test.py
Insert the following Python code into the file:
def app(environ, start_response): start_response(“200 OK”, []) return iter([b”Hello, World!”])
This is the minimum code required by Gunicorn to initiate an HTTP response representing a text string in your web browser. After reviewing the code, save and close the file.
Now start your Gunicorn server, specifying the test Python module and application function within it. Start the server will take over your terminal
: gunicorn -workers=2 test:App exit[2022-08-29 07:09:29 +0000] [10568] [INFO] Home gunicorn 20.1.0 [2022-08-29 07:09:29 +0000] [10568] [INFO] Listening on: http://127.0.0.1:8000 (10568) [2022-08-29 07:09:29 +0000] [10568] [INFO] Using worker: sync [2022-08-29 07:09:29 +0000] [10569] [INFO] Starting worker with pid: 10569 [2022-08-29 07:09:29 +0000] [10570] [INFO] Starting worker with pid
- :
10570
The output confirms that Gunicorn is listening at the default direction of http://127.0.0.1:8000. This is the address you previously configured in your Nginx for proxy settings. If not, go back to your /etc/nginx/sites-available/your_domain file and edit the app_server_address associated with the proxy_pass policy.
Open your web browser and navigate to the domain you set up with
Nginx: your_domain Its reverse proxy Nginx
is now serving its Gunicorn web application server, displaying “Hello World!”.
Conclusion
With this tutorial you have configured Nginx as a reverse proxy to allow access to your application servers that would otherwise only be available locally. In addition, you configured request header forwarding, passing the client header information.
For examples of a complete solution using Nginx as a reverse proxy, see how to serve Flask applications with Gunicorn and Nginx on Ubuntu 22.04 or how to run a Meilisearch frontend using InstantSearch on Ubuntu 22.04.