Cheatsheet

Nginx Directives Cheatsheet

This cheatsheet lists the nginx directives you reach for most when writing a server block: listening, routing with location, serving files, proxying, compression, headers and limits. It is for developers who edit nginx.conf by hand. It gives the default value and allowed context for each directive, which the official docs spread across many module pages. The configs below were run on nginx 1.24.0 and checked with curl.

Quick reference

Core and server directives

Directive Default Context Purpose
`worker_processes` `1` main Number of worker processes. `auto` matches CPU cores
`worker_connections` `512` events Maximum simultaneous connections per worker
`error_log` `logs/error.log error` main, http, server, location Error log path and minimum level
`access_log` `logs/access.log combined` http, server, location Request log. `access_log off;` disables it
`listen` `*:80` as root, `*:8000` otherwise server Address and port, plus `ssl`, `default_server`
`server_name` `""` server Host names this server answers
`root` `html` http, server, location Directory that maps to the URL path
`index` `index.html` http, server, location Files tried for a directory request
`include` none any Pull in another config file or glob

Routing and responses

Directive Context Purpose
`location` server, location Match a request path and apply settings
`try_files file ... uri` server, location Use the first file that exists, else fall back to a URI or `=code`
`return code [text]` server, location, if Send a response or redirect immediately
`rewrite regex replacement [flag]` server, location, if Change the URI with a regular expression
`error_page code ... uri` http, server, location Serve a custom page for a status code
`alias path` location Replace the matched location prefix with a path
`add_header name value [always]` http, server, location Add a response header

Location match types

Form Match type Priority
`location = /path` Exact First. Search stops on a match
`location ^~ /path/` Prefix that skips regex Second. Regex locations are not checked
`location ~ regex` Case-sensitive regex Checked in file order, first match wins
`location ~* regex` Case-insensitive regex Same list as `~`
`location /path/` Plain prefix Longest prefix is remembered, used if no regex matches

Proxying and compression

Directive Default Purpose
`proxy_pass URL` none Forward the request to a backend
`proxy_set_header field value` `Host $proxy_host`, `Connection close` Set a header sent to the backend
`proxy_connect_timeout` `60s` Time to establish the backend connection
`proxy_send_timeout` `60s` Time between two writes to the backend
`proxy_read_timeout` `60s` Time between two reads from the backend
`gzip` `off` Turn on response compression
`gzip_types` `text/html` Extra MIME types to compress. `text/html` is always compressed
`gzip_min_length` `20` Smallest response length, in bytes, that is compressed
`gzip_comp_level` `1` Compression level from 1 to 9
`gzip_vary` `off` Add `Vary: Accept-Encoding`

Limits, keepalive and TLS

Directive Default Purpose
`client_max_body_size` `1m` Largest request body. Larger gets 413
`keepalive_timeout` `75s` How long an idle client connection stays open
`sendfile` `off` Use the kernel `sendfile()` call for static files
`server_tokens` `on` Show the nginx version in `Server` and error pages
`ssl_certificate` none Path to the certificate file
`ssl_certificate_key` none Path to the private key
`ssl_protocols` `TLSv1.2 TLSv1.3` Allowed TLS versions

Common patterns

Check a config before you reload it

nginx -t -c /tmp/ng/nginx.conf
nginx: the configuration file /tmp/ng/nginx.conf syntax is ok
nginx: configuration file /tmp/ng/nginx.conf test is successful

Always run nginx -t before nginx -s reload. It parses the file and reports the first error with a file name and line number, without touching the running server. Without -c, it tests the default config path compiled into your build, which on many Linux packages is /etc/nginx/nginx.conf.

Serve files, an exact route and a prefix route

server {
    listen 8088;
    server_name example.test;
    root /tmp/ng/www;
    index index.html;
    location / { try_files $uri $uri/ =404; }
    location = /exact { return 200 "exact\n"; }
    location ^~ /static/ { return 200 "prefix-static\n"; }
    location ~* \.txt$ { add_header X-Kind text always; try_files $uri =404; }
}
GET /           -> 200 home
GET /exact      -> 200 exact
GET /static/x   -> 200 prefix-static
GET /hello.txt  -> 200, header X-Kind: text
GET /nope       -> 404 Not Found

Each line was produced by a real curl against this config. When several locations could match, the exact match wins first, then a ^~ prefix, then regexes in file order, then the longest plain prefix. That is why /static/x hit the prefix block and /hello.txt hit the regex block. try_files $uri $uri/ =404 checks for a file, then a directory, then returns 404 instead of looping.

Redirect one path with return

location /old { return 301 /new; }
HTTP/1.1 301 Moved Permanently
Location: http://example.test:8088/new

Prefer return over rewrite for simple redirects. It states the status code and target directly, with no regular expression to debug. Note that the Location header became an absolute URL with the server name and port.

Proxy `/api/` to a backend and strip the prefix

upstream backend { server 127.0.0.1:9001; }
location /api/ { proxy_pass http://backend/; proxy_set_header Host $host; }
GET /api/users?id=1 -> backend saw /users?id=1 host=example.test

A proxy_pass with a URI (the trailing /) replaces the part of the path that matched the location. Remove the trailing slash and the backend receives /api/users?id=1 unchanged.

Turn on gzip for text

gzip on;
gzip_types text/plain;
Content-Encoding: gzip
51
3001

A 3,001-byte repetitive text file came back as 51 bytes when the client sent Accept-Encoding: gzip, and as 3,001 bytes without it. Real files compress less, but the header and the size drop are what to check.

Cap upload size

client_max_body_size 1k;
413
2026/10/05 09:24:33 [error] 8186#8186: *1 client intended to send too large body: 3001 bytes, client: 127.0.0.1, server: example.test, request: "POST /hello.txt HTTP/1.1", host: "localhost:8088"

The first line is the status code curl received. The second is the entry in error.log.

Pitfalls

  • Missing semicolon or brace: nginx -t prints [emerg] unexpected "}" in /tmp/ng/bad.conf:18 and test failed. In that run the cause was return 301 /new } with no semicolon. Fix the line named in the message.
  • Upload fails with 413: The default client_max_body_size is 1m. Raise it in the right context (http, server or location) and reload.
  • Trailing slash in proxy_pass: proxy_pass http://backend/; rewrites the path, while proxy_pass http://backend; passes it unchanged. One character changes which URL the backend sees.
  • A regex location beating your prefix: Regex locations are checked after the longest prefix is chosen and win over a plain prefix. Use ^~ on the prefix if a regex such as \.php$ should not take over.
  • add_header disappearing: Per the nginx docs, headers are inherited from the previous level only if the current level defines no add_header of its own. Repeat every header in a location that adds one. Without always, headers are not added to every response code: in a test, a 404 carried neither the http-level X-Top nor a location-level header, while a 200 carried X-Top.
  • Proxy timeouts of 60 seconds: Slow backends hit proxy_read_timeout 60s and the client sees a 504. Raise it for that location only.
  • gzip sending nothing: gzip on; alone compresses only text/html. List other types such as text/plain or application/json in gzip_types.
  • Version leak: With server_tokens on (the default) the Server header and error pages show the nginx version. server_tokens off; reduced the header to Server: nginx in this run.
  • alias and root mixed up: root appends the whole URI to the path, while alias replaces the location prefix. location /bad/ { alias /tmp/ng/al; } returned 404 for /bad/x.txt because nginx looked for /tmp/ng/alx.txt, while location /files/ { alias /tmp/ng/al/; } served the file. Keep the trailing slash on both sides.

Related ZipKit tools

Related cheatsheets