Skip to content

Reverse Proxy & Static Content

Nginx's most basic job is deciding, for every request it receives, whether to answer it directly or hand it off to something else. Two ideas make that possible: the difference between a forward and a reverse proxy, and the server/location block structure nginx uses to route each request.

TL;DR — in 30 seconds:

  • A forward proxy hides the client from the destination; a reverse proxy hides the backend from the client — nginx in front of an app is the reverse case.
  • A server block picks up a request by listen port and server_name; a location block inside it then matches the request's path and decides what happens to it.
  • A location can serve static files straight off disk (via root/index) with no upstream involved at all — the proxying case is covered on the next two pages.

1. Forward proxy vs reverse proxy

A forward proxy sits in front of clients: it forwards their outgoing requests to the internet on their behalf, so the destination server only ever sees the proxy, never the individual client behind it (a corporate network's outbound proxy is the classic example).

A reverse proxy sits in front of servers: it receives incoming client requests and forwards each one to whichever backend server should handle it. The client only ever talks to the proxy — it never sees, and never needs to know, which backend actually answered.

flowchart LR
    subgraph Forward proxy
        c1["Client"] --> fp["Forward proxy"] --> s1["Internet"]
    end
    subgraph Reverse proxy
        c2["Clients"] --> rp["nginx<br/>reverse proxy"] --> s2["Backend server(s)"]
    end

Nginx in front of an app is the reverse case: from the outside, there is only nginx.

2. server blocks — which requests you even see

A server block picks up a request based on the port it's listening on (listen) and the server_name it's configured to answer for (matched against the request's Host header). Nginx can hold many server blocks side by side on the same machine, each answering for a different domain or port.

3. location blocks — what happens to a matched request

Inside a server block, one or more location blocks match on the request's path and decide what happens next: serve a static file straight off disk, or hand the request to something else entirely (a proxy_pass — covered in Proxying & load balancing).

Directive What it decides
listen Which port this server block answers on
server_name Which Host header value this server block answers for
location Which request path, inside a matched server block, this rule applies to
root The directory static files are served from
index The default file to serve when a request names a directory, not a file

4. Serving static content

A minimal static-content server block needs only a handful of directives:

server {
    listen 80;
    server_name example.com;
    root /var/www/html;
    index index.html;

    location /assets/ {
        # served straight from disk — no upstream involved
    }
}

Any request matching /assets/ is answered directly from /var/www/html/assets/ — nginx never forwards it anywhere.

5. How this connects

  • Once a location block decides a request isn't a static file, TLS termination and proxying to an upstream are the next layer — covered in the next two pages.
  • The Host-header matching a server_name performs depends on the HTTP layer covered in the Networking module's HTTP/TCP/SSH page — nginx routing sits directly on top of that.

You can defend this when you can explain, unprompted, why a reverse proxy hides the backend from the client (and what a forward proxy hides instead), and trace which server/location block would handle a given request path.