Skip to main content

Lesson: Web Server & Reverse Proxy

What you'll learn

  • What a web server does and how Nginx fits into a typical app stack.
  • How to install Nginx and serve a static website.
  • How to write server blocks (virtual hosts) to host more than one site.
  • How to put Nginx in front of an application as a reverse proxy.
  • Why reverse proxies exist and how this maps to our lab's edge TLS setup.

By the end you can install Nginx, serve files, and proxy traffic to a back-end app at a recognise-and-operate level.

The lesson

1. What is a web server?

A web server is a program that listens for HTTP requests on a network port (usually port 80 for plain HTTP, 443 for HTTPS) and returns responses — HTML pages, images, JSON, etc. HTTP (HyperText Transfer Protocol) is the request/response language browsers and APIs speak.

Nginx (say "engine-x") is one of the most popular web servers. It does two big jobs:

  1. Serve static files directly from disk (HTML, CSS, JS, images) — very fast.
  2. Act as a reverse proxy: receive a request and pass it on to another program (your application), then return that program's answer to the client.

2. Install Nginx

On Debian/Ubuntu:

sudo apt update
sudo apt install -y nginx
sudo systemctl enable --now nginx     # start now + on every boot
systemctl status nginx                # check it is "active (running)"

Verify it works:

curl http://localhost

You should see the default "Welcome to nginx!" HTML. Key locations to know:

  • /etc/nginx/nginx.conf — main config file.
  • /etc/nginx/sites-available/ — your site config files live here.
  • /etc/nginx/sites-enabled/ — symlinks to the sites that are actually turned on.
  • /var/www/html/ — default web root (where files are served from).
  • /var/log/nginx/access.log and error.log — your first stop when debugging.

3. Serve a static site

Make a folder and an HTML file:

sudo mkdir -p /var/www/mysite
echo '<h1>Hello from the lab</h1>' | sudo tee /var/www/mysite/index.html

Create a server block — a block of config describing one website. Put it in /etc/nginx/sites-available/mysite:

server {
    listen 80;
    server_name mysite.example.com;       # the domain this block answers for
    root /var/www/mysite;                  # where files come from
    index index.html;

    location / {
        try_files $uri $uri/ =404;         # find the file, else return 404
    }
}

Enable it, test the config, reload:

sudo ln -s /etc/nginx/sites-available/mysite /etc/nginx/sites-enabled/
sudo nginx -t            # ALWAYS test before reloading — catches typos
sudo systemctl reload nginx

nginx -t checks syntax. reload applies new config without dropping connections (unlike restart, which stops then starts). Make nginx -t a habit before every reload.

4. What is a reverse proxy, and why?

Most apps (a Node.js, Python, or Java service) can speak HTTP themselves on some port like 3000. So why put Nginx in front?

   Without proxy                  With reverse proxy (Nginx)

  client                          client
    |  :3000                        |  :80/:443
    v                               v
  [ app ]                        [ Nginx ]  <- one front door
                                    |  :3000 (localhost)
                                    v
                                 [ app ]

A reverse proxy sits in front of one or more back-end apps and forwards requests to them. It buys you:

  • One front door on standard ports while apps stay on high/internal ports.
  • TLS termination — decrypt HTTPS once at the edge so apps handle plain HTTP.
  • Load balancing — spread traffic across several app copies.
  • Static file offload, caching, compression, rate limiting — Nginx does these better than most app frameworks.
  • A buffer — the app isn't directly exposed to the raw internet.

(A forward proxy works for outbound clients; a reverse proxy works on behalf of servers. We almost always mean reverse proxy.)

5. Reverse-proxy config

Suppose an app runs on 127.0.0.1:3000. Proxy to it:

server {
    listen 80;
    server_name app.example.com;

    location / {
        proxy_pass http://127.0.0.1:3000;            # send requests here
        proxy_set_header Host $host;                 # preserve original Host
        proxy_set_header X-Real-IP $remote_addr;     # tell the app who called
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

proxy_set_header Host $host matters: many apps decide what to serve based on the Host header (the domain the user typed). Preserving it keeps the app behaving correctly behind the proxy.

Test and reload again:

sudo nginx -t && sudo systemctl reload nginx

6. TLS in our lab — edge termination at HAProxy

In our lab, TLS is terminated at the edge by HAProxy (with a wildcard certificate), not by each Nginx. That means:

  • The encrypted HTTPS connection ends at HAProxy.
  • HAProxy forwards plain HTTP to the app's Nginx, preserving the Host header.
  • Your Nginx server blocks therefore listen 80; and do not need ssl_certificate lines.

So in the lab a typical chain is:

  internet --HTTPS--> [ HAProxy edge (TLS) ] --HTTP+Host--> [ Nginx ] --> [ app ]

Because Nginx receives the real Host header from HAProxy, your server_name matching and proxy_set_header Host $host still work exactly as above. Do not add self-signed certs to lab app Nginx instances — that's the edge's job. (TLS certs and HAProxy/DNS are managed for you.)

7. Day-to-day operating commands

sudo nginx -t                       # validate config
sudo systemctl reload nginx         # apply without downtime
sudo systemctl restart nginx        # full stop/start (rarely needed)
sudo tail -f /var/log/nginx/error.log   # watch errors live

If a site 502s (Bad Gateway), it almost always means Nginx is up but the back-end app it proxies to is down or on the wrong port — check the app first.

Dig deeper

Search terms

  • nginx serve static site ubuntu tutorial
  • nginx reverse proxy explained
  • what is the difference between reload and restart nginx
  • nginx 502 bad gateway troubleshooting
  • nginx proxy_set_header Host explained

Check yourself

  1. What two main jobs does Nginx do, and which port does plain HTTP use by default?
  2. What is the difference between sites-available and sites-enabled?
  3. Why should you run nginx -t before systemctl reload nginx?
  4. In one sentence, what is a reverse proxy and name two benefits it provides.
  5. In our lab, where is TLS terminated, and what does that mean for the listen line in your app's server block?