Skip to main content
Welcome back. In this lesson we’ll configure an Nginx reverse proxy that forwards HTTP requests to two backend Flask applications running on port 5000. This pattern is commonly used for basic load distribution and for isolating public-facing traffic from application hosts. The reverse proxy can run on the same machine as other services to save resources and centralize access control.
A reverse proxy accepts client requests and forwards them to one or more backend servers. Unlike a separate load balancer appliance, the reverse proxy can run on the same Nginx host as other services, providing centralized routing and access control.
A diagram of a reverse proxy setup showing users connecting through a network cloud to an NGINX reverse proxy, which forwards requests to backend Flask web servers. The web servers are running Flask on port 5000.

What you’ll set up

  • Nginx (reverse-proxy) receiving client traffic on port 80.
  • Two Flask backend servers (node01, node02) listening on port 5000.
  • Firewall rules so only the reverse proxy can reach the Flask ports.

Prerequisites

By default, Flask’s development server binds to 127.0.0.1 and is only accessible locally. To allow a remote reverse proxy to connect, run Flask bound to 0.0.0.0, for example: app.run(host='0.0.0.0'). For production, use a production WSGI server such as gunicorn or uWSGI instead of Flask’s built-in server.

Inspect the environment

From the reverse-proxy host (nginx) you should be able to SSH into each backend and confirm the Flask app on port 5000 responds. Example sessions (condensed):
Verify that no web server is listening on port 80 on the backends (only Flask on 5000):

Restrict backend access with UFW

Limit access to port 5000 so only the reverse proxy IP can connect. Replace 192.230.206.12 with your reverse proxy IP.
Repeat the ufw allow command on each backend (node02) as needed.

Configure Nginx on the reverse-proxy host

  1. Remove the default site symlink from /etc/nginx/sites-enabled.
  2. Create a new site configuration file /etc/nginx/sites-available/helloworld.
  3. Enable it with a symlink in /etc/nginx/sites-enabled/.
Example file operations:

Nginx configuration: upstream + server

Create the upstream block listing both Flask backends (port 5000) and a server block that proxies requests to that upstream. Save the following as /etc/nginx/sites-available/helloworld:
Notes:
  • The upstream name is hello_world and proxy_pass references it as http://hello_world.
  • Backend servers are specified with their IPs and port 5000 because Flask listens there.
  • Nginx listens on port 80 and forwards traffic to the upstream backends.

Test and reload Nginx

Always test Nginx configuration syntax before reloading, then simulate client requests using a Host header for helloworld.com.

Simulate a backend failure

To simulate a node being down, comment out or remove one backend from the upstream block and reload Nginx. Nginx will stop sending requests to the removed/commented backend and continue routing to the remaining server(s). Example (one backend commented out):
After reloading:

Conclusion

You now have a working Nginx reverse proxy that forwards HTTP requests to multiple backend Flask apps on port 5000. Key takeaways:
  • Use an upstream block to list backend servers and proxy_pass to route to that upstream.
  • Restrict backend access with ufw so only the reverse proxy can reach application ports.
  • For production deployments, run Flask behind a production WSGI server such as gunicorn or uWSGI.

Watch Video

Practice Lab