Logs
Scope: nginx's two logs — what each is for, how to read a line, and why the error log is the first place a broken site explains itself.
The access log: who asked, what happened
One line per request, in the default "combined" format:
127.0.0.1 - - [20/Oct/2026:20:46:30 +0000] "GET /limited/ HTTP/1.1" 200 32 "-" "curl/8.5.0"
Read left to right: client address, ident and
authenticated user (almost always - -), when, the
request line exactly as sent, the status, the bytes
of the response body, the referer, the user-agent. Set
with access_log in any context; the lab puts it in
http so every server shares it.
The access log answers traffic questions — which paths, which statuses, which clients — and nothing else. It never says why. For why, the other log.
The error log: nginx explaining itself
2026/10/20 20:44:53 [error] 13779#13779: *8 directory index of "/home/you/nginx-lab/www/main/emptydir/" is forbidden, client: 127.0.0.1, server: localhost, request: "GET /emptydir/ HTTP/1.1", host: "127.0.0.1:8080"
Anatomy: date and time, severity in brackets, the
worker PID and connection number, then the message —
and for request-related lines, the scaffolding that
locates it: client, server, request, host, and
for proxied requests an upstream field too. The
message itself is usually the whole diagnosis:
open() "..." failed (2: No such file or directory)
(the 404, with the exact path tried), directory index
... is forbidden (the 403), connect() failed (111:
Connection refused) while connecting to upstream (the
502), limiting requests, excess: 2.580 by zone
"labzone" (chapter 11's rate limit doing its job).
Severities run debug, info, notice, warn,
error, crit, alert, emerg, set as the second
argument of error_log (the lab uses warn, so notices
from reloads stay out of the way but warnings don't).
Startup and reload narration go here too: when a reload
misbehaves, the error log from that minute is the
confession.
Working habits
- Tail both while you test.
tail -fon the error log beside yourcurlmakes cause and effect land side by side; every capture in this guide was read that way. - The status histogram is your smoke alarm.
awk '{print $9}' access.log | sort | uniq -cover the last while tells you the site's shape at a glance — a wall of 502s is a dead app, a wall of 404s from one client is a scanner (or a broken deploy). - Behind a proxy, distrust the access log's first
field. It records the connecting address — which
for anything standing behind another proxy (or a CDN)
is that proxy's. Chapter 7's forwarded headers are how
the real visitor's address gets into the record;
nginx can log
$http_x_forwarded_forwith a customlog_formatwhen you need it on every line. - Logs rotate or disks fill. The Ubuntu package
ships a logrotate rule for
/var/log/nginx/*.log; if you point logs somewhere of your own, rotation becomes your problem to remember.
Check: cause one deliberate failure on your own server — request a file that doesn't exist — and find that exact request in both logs within the same minute. From then on, the error log is a tool you own, not a place you visit in a panic.