Every request a web server handles can be written to an access log. Understanding the access log format lets you spot attacks, find slow pages and prove what happened during an outage. This guide explains the standard format, how to add the fields that matter for tuning, and how to query logs from the shell.
Before you start
- Root access to an Nginx or Apache server. Logs live in
/var/log/nginx/,/var/log/apache2/or/var/log/httpd/. - On cPanel, use cPanel → Metrics → Raw Access; see cPanel error and raw access logs.
The combined log format, field by field
203.0.113.7 - - [14/Nov/2026:10:12:01 +0000] "GET /blog/ HTTP/2.0" 200 18342 "https://google.com/" "Mozilla/5.0 ..."| Field | Example | Meaning |
|---|---|---|
| Client IP | 203.0.113.7 | Visitor (or proxy) address |
| Ident / user | - - | Unused / HTTP auth user |
| Time | [14/Nov/2026:10:12:01 +0000] | When the request finished |
| Request line | GET /blog/ HTTP/2.0 | Method, path, protocol |
| Status | 200 | HTTP status code |
| Bytes | 18342 | Response body size |
| Referer | https://google.com/ | Page that linked here |
| User-Agent | Mozilla/5.0 … | Browser or bot |
Add timing fields
The default format has no timing, which is what you need most when a site is slow.
Nginx
log_format timed '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" rt=$request_time urt=$upstream_response_time cache=$upstream_cache_status host=$host';
access_log /var/log/nginx/access.log timed;$request_time: total seconds from first byte read to last byte sent.$upstream_response_time: time PHP-FPM or the backend took. If this is close to request_time, the app is slow, not the network.
Apache
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %D %{Host}i" timed
CustomLog ${APACHE_LOG_DIR}/access.log timed%D is the time in microseconds.
Write JSON logs
JSON is easier to ship to log tools and to query with jq:
log_format json escape=json '{"time":"$time_iso8601","ip":"$remote_addr","host":"$host","method":"$request_method","uri":"$request_uri","status":$status,"bytes":$body_bytes_sent,"rt":$request_time,"ua":"$http_user_agent"}';Useful one-liners
- Top 10 client IPs:
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head - Most requested URLs returning 404:
awk '$9==404 {print $7}' access.log | sort | uniq -c | sort -rn | head - Requests slower than 2 seconds (with the
timedformat):grep -oP 'rt=\K[0-9.]+' access.log | awk '$1>2' | wc -l - Status code summary:
awk '{print $9}' access.log | sort | uniq -c | sort -rn
Real client IPs behind a proxy
Behind Cloudflare or a load balancer every line shows the proxy IP. Use Nginx's real_ip module or Apache's mod_remoteip with the proxy's address ranges so the first field holds the visitor's IP.
Keep logs under control
Busy sites write gigabytes a day. Make sure logrotate compresses and removes old files (see logrotate guide), and consider disabling access logs for health checks and static assets with access_log off; in those locations.
FAQ: access log format
What is the difference between access and error logs?
Access logs record every request; error logs record problems such as missing files, PHP-FPM failures and config warnings.
Does logging slow the server?
Very little. Use buffer=32k flush=5s in Nginx on very busy sites.
Are IP addresses in logs personal data?
Under GDPR they can be. Keep logs only as long as you need them.
