NGINX Error Guide: 'unexpected end of file, expecting }' — Fix Unbalanced Braces
Fix 'unexpected end of file, expecting }' in NGINX: find the unclosed block or stray brace across included files and balance your { } pairs.
- #nginx
- #web-server
- #troubleshooting
- #errors
Stuck on this NGINX error? Get the free incident triage checklist
A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.
Overview
NGINX prints this during config parsing when it reaches the end of a file while still inside an open block — an unbalanced { }. The message points at the file where parsing ran out, usually with a line number at or near the end:
nginx: [emerg] unexpected end of file, expecting "}" in /etc/nginx/nginx.conf:98
The reported line is where NGINX ran out of input, not necessarily where the mistake is. The actual unclosed block might be dozens of lines earlier — or in a completely different file pulled in via include. NGINX concatenates included files as it parses, so a missing brace in one snippet can surface as an “end of file” error in another.
Symptoms
nginx -tfails with[emerg] unexpected end of file, expecting "}"and a file/line near the end of a file.- Reloads fail; the previously loaded config keeps running (old workers persist).
- The reported line looks fine — it’s the closing brace of the whole file, but a block opened earlier was never closed.
- It appeared right after adding a new
location,if,map, orupstreamblock, or a newincluded file.
Common Root Causes
- A block opened but never closed — a
location,server,http,map,upstream, orifwith a{and no matching}. - An extra or missing brace in an included file — the imbalance lives in
conf.d/*.confor asites-enabledsnippet, but the error is reported innginx.conf. - A brace inside a string that isn’t quoted — a
{in alog_formatoradd_headervalue that NGINX reads as a block opener. - Copy-paste of a partial block — pasting a
location { ...without its closing brace. - Commented-out closing brace — a
#accidentally placed before a}that was needed.
Diagnostic Workflow
The fastest way to localize an imbalance is a brace count per file. Any file where opens and closes don’t match is the suspect:
# Count { and } in every config file; mismatched counts flag the culprit
for f in /etc/nginx/nginx.conf /etc/nginx/conf.d/*.conf /etc/nginx/sites-enabled/*; do
[ -f "$f" ] || continue
o=$(grep -o '{' "$f" | wc -l); c=$(grep -o '}' "$f" | wc -l)
[ "$o" != "$c" ] && echo "IMBALANCE: $f open=$o close=$c"
done
Let nginx -t confirm and, importantly, dump the effective merged config so you can see block nesting across includes:
nginx -t
nginx -T | less # full assembled config with all includes inlined
Read the config as blocks. A well-formed proxy location closes every brace it opens:
server {
listen 443 ssl;
server_name app.example.com;
location /api/ {
proxy_pass http://backend;
proxy_set_header Host $host;
} # <-- this closing brace is the one commonly forgotten
location / {
root /var/www/app;
}
} # <-- and the server block's own closing brace
If a brace legitimately belongs in a value, quote the whole value so NGINX doesn’t treat it as a block:
log_format json '{ "ip":"$remote_addr", "uri":"$request_uri" }';
Re-validate after each change:
nginx -t && nginx -s reload
Example Root Cause Analysis
A reload started failing with unexpected end of file, expecting "}" in /etc/nginx/nginx.conf:96 — the very last line of the main file. Nothing on line 96 was wrong; it was the closing brace of the http {} block. The brace-count loop immediately flagged the real offender: /etc/nginx/conf.d/api.conf had open=7 close=6.
An engineer had added a new location /internal/ { ... } block but, while copy-pasting, dropped its closing }. Because conf.d/*.conf is included inside http {}, NGINX kept parsing past the intended end of that location, consumed the rest of the file and the include boundary, and only noticed something was wrong when it hit the final brace of nginx.conf. Adding the missing } to api.conf fixed it. Running nginx -T afterward confirmed the block nesting was correct in the assembled config.
Prevention Best Practices
- When the error points at the last line of a file, don’t stare at that line — run a brace-count across every config and included snippet to find the real imbalance.
- Use
nginx -Tto view the fully assembled config; it makes cross-file nesting problems obvious. - Edit with an editor that matches and highlights braces, and fold blocks so an unclosed one is visually obvious.
- Add and close a block in the same edit — write
location /x/ { }first, then fill it in, so the closing brace is never forgotten. - Quote any value that legitimately contains
{or}(JSON log formats, some headers) so NGINX doesn’t parse it as a block. - Keep
conf.d/sites-enabledsnippets small and self-contained so a brace error is localized to one reviewable file.
Quick Command Reference
# Find the file with unbalanced braces
for f in /etc/nginx/nginx.conf /etc/nginx/conf.d/*.conf; do
o=$(grep -o '{' "$f" | wc -l); c=$(grep -o '}' "$f" | wc -l)
[ "$o" != "$c" ] && echo "$f open=$o close=$c"; done
# Validate and see the assembled config
nginx -t
nginx -T | less
# Reload after fixing
nginx -t && nginx -s reload
Conclusion
unexpected end of file, expecting "}" means a block was opened and never closed, and NGINX ran off the end of the input still waiting for the brace. The reported line is where parsing stopped, not where the bug is — so count braces across nginx.conf and every included file, use nginx -T to inspect nesting, and quote any braces that live inside string values. Balance the pair, re-run nginx -t, and the reload will succeed.
Fixed it? Get 500 NGINX & DevOps AI prompts — free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.
Did this fix your issue?
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.