Skip to content

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 -f on the error log beside your curl makes 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 -c over 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_for with a custom log_format when 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.