What Is a Reverse Proxy?

If you are new to networking, a reverse proxy sounds more complex than it really is.

In very simple words:

  • A reverse proxy is a middle layer between your browser and an app.
  • You talk to the reverse proxy first.
  • The reverse proxy forwards your request to the real backend service.
  • Then it sends the backend response back to you.

So your browser does not talk directly to the backend. It talks to the reverse proxy.

Simple Diagram

Browser
localhost:8181
Nginx Reverse Proxy
Backend service on TARGET_HOST:TARGET_PORT

Why Use a Reverse Proxy?

Common reasons:

  • Keep one public entry point
  • Route traffic to different backend services
  • Hide backend details from users
  • Add SSL/TLS, caching, or rate limits later

For this post, we keep it minimal and practical.

Local Example (Docker Compose + Nginx)

Scenario:

  • You open http://localhost:8181
  • Nginx listens on port 8181
  • Nginx forwards to a backend service on port 80
  • Backend host and port are configurable from .env

This runs locally on any developer machine with Docker and Docker Compose.

Project Files

Create a folder (for example reverse-proxy-demo) and add these files.

1) docker-compose.yml

services:
  reverse-proxy:
    image: nginx:alpine
    container_name: reverse-proxy
    ports:
      - "8181:8181"
    environment:
      TARGET_HOST: ${TARGET_HOST}
      TARGET_PORT: ${TARGET_PORT}
    volumes:
      - ./nginx.conf.template:/etc/nginx/templates/default.conf.template:ro
    depends_on:
      - backend

  backend:
    image: nginx:alpine
    container_name: demo-backend

What it does:

  • Starts two containers: reverse-proxy and backend
  • Maps your machine port 8181 to Nginx reverse proxy
  • Injects TARGET_HOST and TARGET_PORT from .env
  • Uses the Nginx template file to generate runtime config

2) nginx.conf.template

server {
    listen 8181;
    server_name _;

    location / {
        proxy_pass http://${TARGET_HOST}:${TARGET_PORT};
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

What it does:

  • Nginx listens on 8181
  • Forwards every request to TARGET_HOST:TARGET_PORT
  • Sends useful headers to backend

3) .env

TARGET_HOST=backend
TARGET_PORT=80

What it does:

  • Defines where Nginx should forward traffic
  • Easy to change without editing YAML or Nginx config

Commands to Run

Inside your project folder:

docker compose up -d

Open:

http://localhost:8181

You should see the default Nginx backend page.

Stop everything:

docker compose down

Changing Target Host or Port

Edit .env:

TARGET_HOST=backend
TARGET_PORT=80

Then recreate containers:

docker compose down
docker compose up -d

Quick Troubleshooting

1) Port 8181 is already used

Error usually looks like: bind: address already in use.

Fix:

  • Stop the process using 8181, or
  • Change mapping in docker-compose.yml (example: 8282:8181)

2) Bad Gateway (502)

Usually means Nginx cannot reach backend.

Check:

  • Is backend container running? docker compose ps
  • Are TARGET_HOST and TARGET_PORT correct in .env?
  • Recreate containers after changes: docker compose down && docker compose up -d

3) Config changes not applied

If you changed .env or template and behavior did not change, restart stack:

docker compose down
docker compose up -d

Final Takeaway

A reverse proxy is simply a traffic forwarder in front of your backend.

With Docker Compose + Nginx + .env, you get a clean local setup where backend target host/port are easy to change and test.