> ## Documentation Index
> Fetch the complete documentation index at: https://notes.kodekloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Demo Reverse Proxy

> Guide to configure Nginx as a reverse proxy forwarding HTTP to two Flask backends with firewall restrictions and testing instructions.

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.](https://mintcdn.com/kodekloud-c4ac6d9a/ij3FNIPjp1w0Myd0/images/Nginx-for-Beginners/Intermediate-Config/Demo-Reverse-Proxy/nginx-reverse-proxy-flask-backend-5000.jpg?fit=max\&auto=format\&n=ij3FNIPjp1w0Myd0\&q=85\&s=bd304ef7a1068dacc2a0890ed38fa1e2)

## 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

| Item | Description |
| - | - |
| Reverse proxy host | Hostname: `nginx` — runs Nginx and proxies traffic |
| Backend hosts | `node01`, `node02` — each runs a Flask app on port `5000` |
| SSH access | SSH connectivity from `nginx` to both backends |
| Firewall | `ufw` available on backend nodes to restrict access |

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):

```bash theme={null}
# From reverse proxy server
root@nginx ~ ➜ ssh node01
Welcome to Ubuntu 22.04.5 LTS ...

root@node01 ~ ➜ curl localhost:5000
<h1>Hello, Human!</h1>[Not Authenticated]
root@node01 ~ ➜ exit
logout
Connection to node01 closed.

root@nginx ~ ➜ ssh node02
Welcome to Ubuntu 22.04.5 LTS ...

root@node02 ~ ➜ curl localhost:5000
<h1>Hello, Human!</h1>[Not Authenticated]
root@node02 ~ ➜ exit
```

Verify that no web server is listening on port 80 on the backends (only Flask on 5000):

```bash theme={null}
root@node01 ~ ➜ curl localhost
curl: (7) Failed to connect to localhost port 80: Connection refused

root@node01 ~ ➜ curl localhost:5000
<h1>Hello, Human!</h1>[Not Authenticated]
```

## 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.

```bash theme={null}
# Check current UFW status
root@node01 ~ ➜ ufw status
Status: active

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW       Anywhere
22/tcp (v6)                ALLOW       Anywhere (v6)

# Allow reverse proxy IP to reach port 5000
root@node01 ~ ➜ ufw allow from 192.230.206.12 proto tcp to any port 5000
Rule added

# Confirm the rule
root@node01 ~ ➜ ufw status
Status: active

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW       Anywhere
5000/tcp                   ALLOW       192.230.206.12
22/tcp (v6)                ALLOW       Anywhere (v6)
```

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:

```bash theme={null}
# Move into Nginx site config directories
root@nginx /etc/nginx/sites-enabled ➜ ll

# remove default symlink
root@nginx /etc/nginx/sites-enabled ➜ rm default

root@nginx /etc/nginx/sites-available ➜ ll

# create the new configuration file
root@nginx /etc/nginx/sites-available ➜ vim helloworld

# enable the site by creating a symlink
root@nginx /etc/nginx/sites-available ➜ ln -s /etc/nginx/sites-available/helloworld /etc/nginx/sites-enabled/helloworld
```

### 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`:

```nginx theme={null}
# Upstream configuration
upstream hello_world {
    server 192.230.206.3:5000;
    server 192.230.206.6:5000;
}

# Server configuration
server {
    listen 80;

    root /var/www/html;
    index index.html index.htm index.nginx-debian.html;

    server_name helloworld.com;

    location / {
        proxy_pass http://hello_world;
    }
}
```

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`.

```bash theme={null}
# Test and reload Nginx
root@nginx /etc/nginx/sites-available ➜ nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

root@nginx /etc/nginx/sites-available ➜ nginx -s reload

# Test via curl using Host header (simulates DNS)
root@nginx /etc/nginx/sites-available ➜ curl --header "Host: helloworld.com" localhost
<h1>Hello, Human!</h1>[Not Authenticated]

root@nginx /etc/nginx/sites-available ➜ curl --header "Host: helloworld.com" localhost/foo
<h1>Foo page</h1><a href="/do-something?next=/foo?">Do something and redirect</a>

root@nginx /etc/nginx/sites-available ➜ curl --header "Host: helloworld.com" localhost/bar
<h1>Bar page</h1><a href="/do-something?next=/bar?">Do something and redirect</a>
```

## 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):

```nginx theme={null}
# Upstream configuration
upstream hello_world {
    server 192.230.206.3:5000;
    #server 192.230.206.6:5000;
}
```

After reloading:

```bash theme={null}
root@nginx /etc/nginx/sites-available ➜ nginx -s reload
root@nginx /etc/nginx/sites-available ➜ curl --header "Host: helloworld.com" localhost
<h1>Hello, Human!</h1>[Not Authenticated]
```

## 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`.

## Links and references

* [Flask Documentation](https://flask.palletsprojects.com/)
* [Nginx Documentation](https://nginx.org/en/docs/)
* [UFW (Uncomplicated Firewall) Help — Ubuntu Community](https://help.ubuntu.com/community/UFW)

> **[Watch Video](https://learn.kodekloud.com/user/courses/nginx-for-beginners/module/c78ff9cb-c15d-4f85-92fc-abee5ed98b20/lesson/85497d1b-b7c7-46d0-a176-56ec8041abff)**

> **[Practice Lab](https://learn.kodekloud.com/user/courses/nginx-for-beginners/module/c78ff9cb-c15d-4f85-92fc-abee5ed98b20/lesson/236908f4-ff1d-4bd9-8ff8-ffa770855e35)**


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.