Configuration Basics

A complete server block for serving static files, plus the core listen, server_name and location directives.

A complete server block

Here is a full, working server block that serves a static site — the kind of config you'd write for a plain HTML/CSS/JS site with no backend application:

Nginx
# /etc/nginx/sites-available/mysite.conf
server {
    listen 80;
    server_name example.com www.example.com;

    root /var/www/mysite/public;
    index index.html index.htm;

    location / {
        try_files $uri $uri/ =404;
    }

    location /static/ {
        expires 30d;
        add_header Cache-Control "public, immutable";
    }

    access_log /var/log/nginx/mysite.access.log;
    error_log  /var/log/nginx/mysite.error.log;
}

The core directives

listen — which port (and optionally which IP address) this server block accepts connections on.

Nginx
listen 80;              # any IP, port 80
listen 127.0.0.1:8080;  # only localhost, port 8080
listen 443 ssl;         # HTTPS

server_name — which Host header(s) this block responds to. Nginx can host many domains on one IP address; when a request comes in, Nginx picks the server block whose server_name best matches the request's Host header.

Nginx
server_name example.com www.example.com;
server_name *.example.com;     # wildcard subdomain
server_name "";                # matches requests with no/unrecognized Host header

root — the filesystem directory that URL paths are resolved against. A request for /about.html on the config above resolves to /var/www/mysite/public/about.html.

index — the file to serve when a request maps to a directory rather than a specific file. index index.html index.htm; means: for a request to /, try index.html first, then index.htm.

The location block

A location block matches an incoming request's URL path and decides how to handle it. Nginx supports several match types:

Nginx
location / {
    # matches everything not matched more specifically elsewhere (prefix match)
    try_files $uri $uri/ =404;
}

location /api/ {
    # prefix match — matches any path starting with /api/
    proxy_pass http://backend;
}

location = /health {
    # exact match — only /health, nothing else
    return 200 "OK";
}

location ~ \.php$ {
    # regex match (case-sensitive) — any path ending in .php
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}

location ~* \.(jpg|jpeg|png|gif)$ {
    # regex match (case-insensitive)
    expires 30d;
}

try_files $uri $uri/ =404; is the standard pattern for a static site: try the exact file ($uri), then try it as a directory ($uri/), and if neither exists, return a 404 instead of leaking a filesystem error.

Common mistakes

  • Forgetting the trailing slash consistency between location and proxy_pass targets — it changes whether the matched prefix is stripped from the forwarded path (a classic, hard-to-spot bug).
  • Using a regex location for everything out of habit — prefix matches are simpler, faster, and sufficient for most routing needs.
  • Not setting expires/Cache-Control on static assets — browsers re-request unchanged files on every visit instead of caching them.
  • Editing the config and forgetting nginx -t && systemctl reload nginx — changes don't take effect until reloaded.