GrepBoard/Manuals
47 manuals ·

Find which paths return 5xx in an Nginx access log

·Nginx combined log format · GNU awk 5.2 · generated test log (Ubuntu 24.04)·5 steps

Count server errors per path with awk, merge query strings, and narrow by status code and time window — one-liners for the default combined format.

In the default combined format the status code is field 9, the request path is field 7, and the time is field 4. The output below comes from a test log generated for this page (one day, 2026-10-17, 578 lines, 58 of them 5xx), shortened to access.log. On a server the file is usually /var/log/nginx/access.log.

[1] Count 5xx responses per path.

console
$ awk '$9 ~ /^5/ {print $7}' access.log | sort | uniq -c | sort -rn | head     41 /api/upload      6 /api/search?q=log      3 /login      2 /api/search?q=nginx      2 /api/search?q=awk      2 /api/search?q=a+b      2 /api/items/7

[2] Drop the query string so /a?x=1 and /a?x=2 count as one path. The /api/search lines above merge into one.

console
$ awk '$9 ~ /^5/ {split($7, p, "?"); print p[1]}' access.log | sort | uniq -c | sort -rn | head     41 /api/upload     12 /api/search      3 /login      2 /api/items/7

[3] Break the errors down by status code. 502 and 504 mean the upstream app did not answer or was too slow; 500 is an error the app returned itself.

console
$ awk '$9 ~ /^5/ {print $9}' access.log | sort | uniq -c | sort -rn     22 500     20 502     11 504      5 503

[4] Count per hour to find the spike. In field 4, [17/Oct/2026:10:05:12, characters 14–15 are the hour.

console
$ awk '$9 ~ /^5/ {print substr($4, 14, 2)}' access.log | sort | uniq -c      1 03      3 09     39 10     10 11      3 15      1 16      1 21

[5] Keep only the busy hour (10:00) and count per path again.

console
$ awk '$4 ~ /17\/Oct\/2026:10:/ && $9 ~ /^5/ {split($7, p, "?"); print p[1]}' access.log | sort | uniq -c | sort -rn     28 /api/upload      9 /api/search      2 /login

Analyze your Nginx log in the browser ↗ — nothing is uploaded.

#nginx#awk#logs#5xx