Reading Application Error Logs: Nginx, Apache & System Logs

A quick-reference guide to reading Nginx, Apache, and Linux syslog error lines: what each field means, with real annotated examples.

Anatomy of an error log line: annotated Nginx, Apache, and Linux syslog examples

Contents

Table of Contents

Every error log line, whether it comes from Nginx, Apache, or the Linux system log, is built from the same four pieces: a timestamp, a severity or level, a source (which process or worker wrote it), and a message. What changes is the punctuation and field order each tool uses to write that down. Once you know the skeleton, you can read a log line from a tool you’ve never used before just by pattern-matching against one you already know.

What’s the same across Nginx, Apache, and syslog error lines?

All three formats descend from the same idea: a fixed severity scale borrowed from syslog itself. Nginx’s own documentation lists eight levels for its error_log directive, in order of increasing severity: debug, info, notice, warn, error, crit, alert, emerg. Apache’s LogLevel directive uses the same eight names (plus extra trace1-trace8 levels for very verbose debugging). Syslog is where this scale originated in the first place. Learn it once and it applies everywhere.

Below is what that shared skeleton looks like across three real log lines, all from the same two-minute window, all from the same client IP. Read on their own, each looks like routine noise. Read together, they’re a single incident: a probe against a checkout endpoint, a path-traversal attempt, and an SSH brute-force try.

Anatomy of an error log line: three annotated log lines from Nginx, Apache, and Linux syslog, color-coded by field type (timestamp, severity, process/PID, client, host, message), showing how the same client IP appears across all three within a two-minute window

How do you read an Nginx error log line?

A typical Nginx error log line looks like this:

2026/03/02 09:14:07 [error] 8842#0: *193 connect() failed (111: Connection refused) while connecting to upstream, client: 203.0.113.44, server: shop.example.com, request: "POST /api/checkout HTTP/1.1", upstream: "http://127.0.0.1:9000/api/checkout", host: "shop.example.com"
FieldExampleWhat it means
Timestamp2026/03/02 09:14:07When Nginx logged the event, in local server time
Level[error]Severity, one of the eight levels above
PID#TID8842#0The worker process ID and thread ID that handled the request
Connection ID*193An internal counter; every log line from the same connection shares this number, useful for grepping one request’s full trail
client203.0.113.44The IP address that made the request
servershop.example.comWhich virtual host (server_name) received it
request"POST /api/checkout HTTP/1.1"The actual HTTP request line
upstream"http://127.0.0.1:9000/api/checkout"The backend Nginx was proxying to, if the error happened during proxying

This particular line means the backend at 127.0.0.1:9000 refused the connection (111: Connection refused) when Nginx tried to proxy a checkout request to it. That’s a backend-down problem, not a client problem. For the full set of common Nginx error messages and what causes each one, see our Nginx error log troubleshooting guide.

How do you read an Apache error log line?

Apache’s default error log format looks different but carries the same information:

[Mon Mar 02 09:15:33.821904 2026] [core:error] [pid 8842] [client 203.0.113.44:51022] AH00126: Invalid URI in request GET /../../../etc/passwd HTTP/1.1
FieldExampleWhat it means
Timestamp[Mon Mar 02 09:15:33.821904 2026]Logged with microsecond precision
Module:Level[core:error]Which Apache module raised it, and its severity
PID[pid 8842]The process ID that handled the request
client[client 203.0.113.44:51022]Client IP and source port
AH codeAH00126Apache’s own numbered error catalog ID, searchable directly in Apache’s documentation
messageInvalid URI in request GET /../../../etc/passwd HTTP/1.1What actually happened

AH00126 specifically means Apache rejected a malformed or suspicious URI before it ever reached a handler, in this case a directory-traversal attempt (../../../etc/passwd) from the same IP that had just been probing the checkout endpoint on Nginx a minute earlier. The AH##### codes are unique to Apache; nothing else uses them, which makes them a fast way to search Apache’s own docs for an exact explanation instead of guessing from the message text alone. For a deeper walkthrough of Apache logging, including access logs and performance troubleshooting, see our complete Apache logs guide.

How do you read a Linux syslog line?

System-level logs, whether from /var/log/syslog, /var/log/messages, or journalctl on a systemd host, follow the older and simpler syslog format:

Mar 2 09:16:02 web-01 sshd[8842]: Failed password for invalid user admin from 203.0.113.44 port 51022 ssh2
FieldExampleWhat it means
TimestampMar 2 09:16:02No year and no timezone by default, a well-known syslog quirk that gets fixed by journalctl’s output or by switching to RFC 5424-style logging
Hostweb-01Which machine generated the line, which matters once logs from many hosts are centralized in one place
Process[PID]sshd[8842]The program and process ID that wrote the line
MessageFailed password for invalid user admin from 203.0.113.44 port 51022 ssh2Free text, entirely defined by whatever program wrote it, with no fixed schema

This line means the same IP that probed the checkout endpoint and attempted a path traversal also tried an SSH login with an invalid username, all within about two minutes. Individually, none of these three log lines look urgent. Correlated by IP and time, they’re a single reconnaissance-to-attack sequence. For where system logs live by default and how to navigate them, see our guide to /var/log; for the specific syntax differences between syslog formats, see syslog formats explained.

Conclusion: the bottom line

Every error log format looks intimidating the first time you see it, but all of them boil down to the same four fields written in a different order: when, how severe, from what, and what happened. Once that skeleton clicks, a new log format from a tool you’ve never used is a five-minute read, not a new skill.

The harder problem is the one this example showed: no single log file tells you the whole story. The Nginx line, the Apache line, and the syslog line each look like isolated noise on their own, and manually correlating them by IP and timestamp across three separate files doesn’t scale past a handful of incidents. Last9’s Logs module supports live tail and real-time search across logs, with correlation to the metrics and traces from the same request, so a sequence like this one shows up as one connected trail instead of three files you have to grep separately. Once you’re reading logs like these regularly, log rotation becomes the next thing worth getting right. Our guide to log rotation in Linux covers logrotate setup so these files don’t fill the disk before you get to read them.

FAQ

What does [error] mean in an Nginx log line?

[error] is one of eight severity levels Nginx’s error_log directive supports, in increasing order of severity: debug, info, notice, warn, error, crit, alert, emerg. Setting a log level captures messages at that level and every level above it, so the default error level also logs crit, alert, and emerg events.

What is an AH##### code in Apache error logs?

AH##### is Apache’s own internal error catalog ID, introduced to make specific error messages searchable and unambiguous. AH00126, for example, always means “Invalid URI in request,” regardless of which specific malformed URL triggered it. Apache maintains these as a fixed, documented list.

Where are Nginx, Apache, and system logs stored by default?

Nginx typically writes to /var/log/nginx/error.log. Apache writes to /var/log/apache2/error.log on Debian and Ubuntu, or /var/log/httpd/error_log on RHEL and CentOS-based systems. System logs live at /var/log/syslog on Debian and Ubuntu, /var/log/messages on RHEL-based systems, and are also queryable through journalctl on any modern systemd-based Linux distribution.

What’s the difference between an error log and an access log?

An access log records every request a web server receives, successful or not, typically including the client IP, request line, response status code, and response size. An error log records only the requests or events that went wrong. Nginx and Apache both maintain the two as separate files; syslog has no equivalent split, since it’s a general-purpose logging channel for system services rather than a request-response log.

How do I watch these logs in real time?

tail -f /var/log/nginx/error.log (or the equivalent path for Apache) streams new lines as they’re written, which is the standard way to watch a log file live during debugging. For systemd services, journalctl -u <service-name> -f does the same thing against the journal instead of a flat file.

Do syslog messages have severity levels like Nginx and Apache do?

Yes, and it’s the same underlying scale: syslog is where the eight-level severity system (emerg through debug) originated, standardized in RFC 5424, the IETF’s Syslog Protocol specification. Nginx and Apache both adopted syslog’s severity naming for their own log levels, which is why the same eight words show up across all three formats even though the log line syntax itself looks different.

What is journalctl and how is it different from /var/log/syslog?

journalctl is the command-line tool for reading the systemd journal, a structured, indexed, binary log store used by modern Linux distributions instead of (or alongside) flat text files like /var/log/syslog. It supports filtering by service, time range, and priority directly, and it also adds the year and timezone that classic BSD-style syslog lines omit by default.

About the authors
S

Sejal Pandey

Sejal Pandey works on content and growth at Last9, writing about observability, reliability, and SRE practices.

Last9 logo and enter key

Start observing for free. No lock-in.

OpenTelemetry · Prometheus

Just update your config. Start seeing data on Last9 in seconds.

Datadog · New Relic · Others

We've got you covered. Bring over your dashboards & alerts in one click.

Built on Open Standards

100+ integrations. OTel native, works with your existing stack.