Reading Web Server Logs: Apache & Nginx

Logging & Log Analysis

Chapter 4 · Reading Web Server Logs: Apache & Nginx

Chapter 3 found the files. This chapter reads what's actually in them — the access log's field-by-field structure, what HTTP status codes really tell you, a genuinely easy-to-miss gap in the default format, and the error log that fills that gap in. Apache and Nginx are covered together throughout, since their conventional log formats are close enough to read with one shared mental model.

The Access Log: Common & Combined Format

Both Apache and Nginx default to a format descended from the same standard, Common Log Format (CLF), almost always extended in practice to Combined Log Format, which adds two more fields. A single line looks like this:

192.168.1.10 - - [08/Aug/2026:14:32:07 +0100] "GET /login HTTP/1.1" 500 1204 "https://example.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
FieldExample valueWhat it means
Remote host192.168.1.10The IP address the request came from
Identity-Almost always a literal dash — rarely used in practice
Authenticated user-A dash unless the request used HTTP basic authentication
Timestamp[08/Aug/2026:14:32:07 +0100]When the request was received, including timezone offset
Request line"GET /login HTTP/1.1"Method, path, and protocol version, together
Status code500The HTTP status code returned — covered in depth below
Response size1204Bytes sent in the response body
Referer"https://example.com/dashboard"The page the request was linked from, if any (Combined format only)
User-agent"Mozilla/5.0..."The requesting browser/client's own self-reported identity (Combined format only)

HTTP Status Codes: Reading the Middle Field

RangeCategoryCommon examples
2xxSuccess200 OK
3xxRedirection301/302 (moved, found)
4xxClient error — the request itself was the problem404 Not Found, 401 Unauthorized, 403 Forbidden
5xxServer error — the server failed to handle a valid request500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable, 504 Gateway Timeout

That 4xx/5xx split matters for triage: a spike in 404s often means a broken link or a bot scanning for pages that don't exist — rarely a real system problem. A spike in 5xx codes means the server itself is failing to do its job, and is exactly the kind of signal Chapters 6 and 7 build a full diagnostic process around.

The Gap Almost Everyone Assumes Isn't There: Response Time

Default access logs don't include how long a request took
Neither Common nor Combined Log Format includes response time at all. Diagnosing "the site is slow" from the access log requires that timing actually be logged in the first place — which means checking, before anything else, whether it's been added to the server's own log format configuration.
ServerDirective to add response time
Apache%D (microseconds) or %T (seconds), added to a custom LogFormat
Nginx$request_time, added to a custom log_format directive

With %D added, the same request from earlier might look like this — the final field, 4523912, is microseconds, meaning this request took roughly 4.5 seconds:

192.168.1.10 - - [08/Aug/2026:14:32:07 +0100] "GET /login HTTP/1.1" 500 1204 4523912

The Error Log: Where the "Why" Often Lives

The access log tells you what request came in and what status went out. It rarely tells you why a request failed — that's the error log's job.

[Sat Aug 08 14:32:07.123456 2026] [php:error] [pid 12345] [client 192.168.1.10:54321] PHP Fatal error: Uncaught mysqli_sql_exception: Connection refused in /var/www/html/login.php:42
2026/08/08 14:32:07 [error] 12345#0: *67 connect() failed (111: Connection refused) while connecting to upstream, client: 192.168.1.10, server: example.com, request: "GET /login HTTP/1.1", upstream: "fastcgi://127.0.0.1:9000"

Both examples describe the same underlying situation — the web server couldn't reach a backend it depends on — from Apache's PHP module and Nginx's own upstream-proxy perspective respectively. Neither the Apache nor the Nginx access log line for this same request would show anything more specific than a 500 or 502; the error log is what actually names the cause.

Reading Both Logs Together

The real technique, used throughout Chapters 6 and 7, is straightforward once both formats are familiar: find the access log line for the problem request, note its exact timestamp, then search the error log for entries at that same moment. The access log confirms that something failed and roughly how; the error log — when the failure produced one — explains why.

Timestamps are the join key between logs
There's no formal database-style link between an access log line and an error log line — the timestamp is the only thing connecting them. This is exactly why Chapter 1's advice to check the timestamp first, and Chapter 3's note on confirming a server's clock/timezone is correct, both matter well beyond a single log file in isolation.

Hands-On Exercises

Exercise 1

A colleague says "I'll just check the access log to see how slow that request was." Explain why this chapter says that might not be possible, and what needs to be true first.

📄 View solution
Exercise 2

An access log shows a request returned a 404, and a separate request returned a 500. Explain which one is more likely to indicate a genuine server-side problem worth investigating, and why.

📄 View solution
Exercise 3

Explain what actually connects a specific access log line to the relevant error log line for the same failed request, given that this chapter says there's no formal link between the two files.

📄 View solution

Chapter 4 Quick Reference

  • Combined Log Format — host, identity, user, timestamp, request line, status, size, referer, user-agent, in that order
  • 2xx success, 3xx redirect, 4xx client error, 5xx server error — a 5xx spike is the one worth real investigation
  • Response time is not included by default — Apache needs %D/%T, Nginx needs $request_time, added explicitly to a custom log format
  • The error log explains why a request failed; the access log only confirms that it did
  • Timestamps are the only link between an access log line and its corresponding error log entry — there's no other formal connection
  • Next chapter: Reading Authentication & Login Logs