2.1 The Configuration Language
The grammar of everything that follows. Short lesson, permanent payoff.
Basic
What you will learn
Directives, blocks, and contexts — the three atoms of NGINX config.
The six syntax rules that cover 99% of what you'll ever write.
Where each context may live, and what may live in each context.
How errors actually present themselves (three real ones, translated once).
Units, flags, and the value syntax you'll meet forever (
on/off,1m,10k,30s,off).
1. The three atoms
An NGINX configuration is a tree. Everything you will ever write is one of three things:
directive → worker_processes auto; (name, value(s), semicolon)
block → events { … } (a context: braces)
directive
with block → limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
(some directives take both a body and a header line)A directive is
name value;— the semicolon is mandatory; NGINX's parser is famously unforgiving.A block
{ … }opens a context. The context determines what the directives inside mean and where their values flow.The file's top level (outside any block) is the main context.
The contexts you will use daily, in nesting order:
main
├── events { } — connection-level (per worker) settings
├── http { } — everything HTTP; only ONE allowed
│ ├── server { } — one virtual host (listen + server_name)
│ │ └── location { } — "if the URI looks like this…"
│ ├── upstream { } — a named backend pool (Module 5)
│ └── map / geo / limit_req_zone / … — http-level variables & zones
└── stream { } — Layer-4 TCP/UDP (Module 10)
└── server { }2. The six syntax rules
Rule 1 — semicolons end every directive. Even the last one in a block. The classic failure: a missing
; makes the parser glue the next line into the value, and the error message points at the
wrong line.
Rule 2 — braces delimit contexts, and their contents are indented by convention only. NGINX ignores whitespace inside lines but not structure.
Rule 3 — values are space-separated words.
listen 8200; # one value
add_header X-Server alpha always; # three values
root /var/www/site; # one value (no quotes needed)
return 200 "matched: exact\n"; # one value, quoted because of spaces/\nQuoting is required only when a value contains spaces or special characters — but professional configs quote any header value as a habit.
Rule 4 —
#
starts a comment. To end of line. There are no block comments. (A
# inside a quoted string is literal.)
Rule 5 — variables are
$name
, evaluated per request (except in a few string-literal places like
log_format… which is exactly where they
are evaluated per request — Lesson 2.4). Config files cannot do arithmetic or loops;
map (Lesson 2.4) and njs (Module 15) are the escape hatches.
Rule 6 — some directives are arrays (repeatable), most are not. Repeating a singleton directive in the same context is an error (
"root" directive is duplicate); repeating an array directive (
add_header,
listen,
server_name,
include,
access_log, …) appends. This distinction becomes
the inheritance rule in Lesson 2.5.
3. What lives where — the context map
Every directive has a list of legal contexts. Violations fail at parse time with a clear message. The ones you'll actually consult:
Directive | main | http | server | location |
|---|---|---|---|---|
| ✅ | |||
| ✅ | ✅ | ✅ | ✅ |
| ✅ | ✅ | ✅ | |
| ✅ | ✅ | ✅ | |
| ✅ | |||
| ✅ | ✅ (nested) | ||
| ✅ | ✅ | ✅ | |
| ✅ | ✅ | ✅ | |
| ✅ | |||
| ✅ | ✅ | ✅ | |
| events only |
Two asymmetries worth internalizing now:
listenandserver_nameare server-only — they define what a server is; there's nothing to inherit.Zone- creators (
limit_req_zone,map,proxy_cache_path) are http-only — they allocate shared memory at config load; consumers (limit_req, the$variable,proxy_cache) are the per-request users.
4. Three real errors, read like a native
These three failures are real — each occurred while building a working config. Each error message is worth translating once, slowly:
(a) Wrong number of arguments:
2026/09/22 20:24:24 [emerg] 2701#2701: invalid number of arguments in "return" \
directive in …/conf/nginx.conf:117Cause:
return 200 "a\n" "b\n"; — the author assumed string concatenation. NGINX has no implicit concatenation; each quoted string is a separate argument. Fix: one string.
Lesson: the parser counts arguments strictly; line 117 is where the
value ended, not where your intent ended.
(b) A compiled-in default leaking into a test run:
nginx: the configuration file … syntax is ok
2026/09/22 20:24:37 [emerg] 2768#2768: open() "/var/log/nginx/access.log" \
failed (2: No such file or directory)Cause: the config set
access_log in
servers but not at
http level. The http-level default — the
compiled
--http-log-path=/var/log/nginx/access.log (Lesson 1.3) — fired first, and on a prefix-isolated instance that path doesn't exist.
nginx -t opens log files as part of testing, so this is a
test failure, not just noise. Fix:
access_log logs/access.log main; at http level.
Lesson: "syntax is ok" ≠ "config is sound" —
-t also exercises files and directories.
(c) A directive in the wrong context:
[emerg] "location" directive is not allowed here in …/conf/nginx.conf:88Self-explanatory once you have §3's table — and note it's
[emerg], i.e. fatal, reported with the exact line.
5. Units, flags, and value dialects
NGINX has small value languages inside the big one. Learn them once:
Kind | Examples | Notes |
|---|---|---|
flags |
| literal words;
|
time |
| no space; suffixes compose (
|
size |
| k=1024, m=1024² — not SI units |
rate |
| requests per second/minute (Module 7) |
offsets |
| dimensionless "requests" |
lists |
| space-separated multiple values |
|
| quoted when spaces/escapes needed |
Time and size units trip people in
reviews, not in errors:
1m of memory is a MiB;
10m of
keepalive_timeout is ten minutes. When a directive accepts both ("
10m or
1h"), the parser is unit-aware; when a value is
off, it usually means "disable this whole feature".
Try it yourself
Everything here is one
vi away from being tested — your nginx.conf is a living grammar playground:
Break Rule 1 on purpose. In
/etc/nginx/nginx.conf, delete the semicolon afterworker_processes auto— the parser will report the error on the following line. Observe, restore, re-verify:
nginx -tReproduce error (b). Comment out the http-level
access_logline, run-t, read the message. Restore it.Watch arguments get counted. Change a
return 200 "…";intoreturn 200 "a" "b";and read the exact error from §4(a).Confirm context policing. Move
listen 8200;into thehttp {}level and enjoy the parser's refusal. Restore.Re-verify when done — every experiment must end with a config that passes nginx -t again:
Q&A
Q1. Why does NGINX report a missing semicolon on the wrong line?
Answer
The parser is greedy across newlines — it happily continues collecting the directive's value words from the next line, and only fails when it hits a token that can't be part of the (now over-long) directive. The reported line is where the grammar broke, not where your intent did.
Q2.
keepalive_timeout 65; — 65 what? And
client_body_buffer_size 16k — 16 of what?
Answer
65 seconds (plain numbers default to seconds for time directives) and 16 KiB (k = 1024). Both defaults come from the same "convenient small-number" philosophy; both are quoted from memory throughout the series.
Q3. You see
limit_req_zone inside a
server block. Will NGINX start?
Answer
No —
[emerg] "limit_req_zone" directive is not allowed here. Zone creators are http-only because the master allocates their shared memory once at config load; a per-server zone would be meaningless before any server exists. (Module 7 makes this architectural reason concrete.)
Q4. True/false:
include is a context.
Answer
False —
include is an
array directive (repeatable) whose value is a glob. It textually splices other files in at exactly its own context level. Lesson 2.6 is devoted to exploiting that.
Takeaways
Three atoms: directive, block, context; one tree walked per request.
Six syntax rules cover nearly everything; the parser is strict, line numbers are approximate,
nginx -tis your only sane workflow.Every directive has legal contexts; creators of shared state are http-only, identity directives (
listen/server_name) are server-only."Syntax is ok" is weaker than "test is successful" —
-talso opens logs and creates temp dirs.Learn the value dialects (flags,
s/m/h,k/m/g,r/s) once; they are uniform across all ~700 directives.
Thanks for your feedback.
Mark this learning resource complete to track your learning progress.