Apache Log Files Explained: How to Read Access and Error Logs
Apache writes two logs: the access log records one line per request (client IP, time, URL, status code, bytes, referrer, user agent) and the error log records faults such as PHP fatals and config errors. On Ubuntu they sit in /var/log/apache2/, on RHEL-family servers in /var/log/httpd/, and on cPanel under ~/access-logs.
For site owners and VPS admins who need to trace a 500 error, count 404s or spot bots from Apache's raw access and error logs.
knowledge
- Read time
- 4 min
- Sections
- 6
- Updated
- 14 Nov 2026
- Written by
- WebJi® team
Last editorial update: 14 November 2026.
What are Apache's access log and error log?
Apache keeps two separate records: the access log writes one line for every request it serves, and the error log records what went wrong while serving them. The access log tells you what happened; the error log tells you why.
| Attribute | Access log | Error log |
|---|---|---|
| Directive | CustomLog + LogFormat | ErrorLog + LogLevel |
| One entry per | Request | Event (fault, warning, notice) |
| Typical content | IP, time, URL, status, bytes, referrer, browser | Module, severity, PID, client, message |
| Main use | Traffic, 404s, bots, attack patterns | 500 errors, PHP fatals, config and startup problems |
Where are Apache log files stored?
The location depends on who manages the server: cPanel exposes logs inside your home directory, while a self-managed VPS keeps them under /var/log with a distro-specific folder name.
- cPanel shared hosting:
~/access-logs/links to the current raw access logs per domain;~/logs/holds archived.gzcopies if archiving is switched on in Raw Access. The cPanel Errors page shows the most recent 300 error-log entries. - Ubuntu and Debian VPS:
/var/log/apache2/access.logand/var/log/apache2/error.log; each virtual host may add its own files there. - AlmaLinux, Rocky and other RHEL-family VPS:
/var/log/httpd/access_logand/var/log/httpd/error_log. - Custom virtual hosts: run
grep -RiE "CustomLog|ErrorLog" /etc/apache2/ /etc/httpd/ 2>/dev/nullto see the paths each site really uses.
PHP can also write its own error_log file inside the site folder, separate from Apache's. The PHP error log guide covers that file, and checking WordPress error logs covers debug.log.
How to read an Apache access log line field by field
Most servers use the "combined" format: seven Common Log Format fields followed by the referrer and the user agent. Apache's default definition is shown first; Debian swaps %b for %O, which counts bytes including headers.
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
203.0.113.9 - - [14/Nov/2026:10:14:02 +0530] "GET /pricing/ HTTP/1.1" 200 18421 "https://www.google.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
| Token | Field | Example value |
|---|---|---|
%h | Client IP (the proxy's IP if behind Cloudflare or Nginx, unless mod_remoteip is set) | 203.0.113.9 |
%l | RFC 1413 identity, almost always unused | - |
%u | HTTP-authenticated user | - |
%t | Time the request was received, with offset | 14/Nov/2026:10:14:02 +0530 |
"%r" | Request line: method, path, protocol | GET /pricing/ HTTP/1.1 |
%>s | Final status after internal redirects | 200 |
%b | Response body size in bytes (- when zero) | 18421 |
%{Referer}i | Page that linked to this URL | https://www.google.com/ |
%{User-Agent}i | Browser or bot string | Mozilla/5.0 (Windows NT 10.0…) |
Read as a sentence: a Windows visitor arriving from Google loaded /pricing/ at 10:14 IST and received 18,421 bytes with status 200.
How to read an Apache 2.4 error log entry
Each Apache 2.4 error entry carries a timestamp, the module and severity, the process ID, the client address and the message, in that order. The severity tells you how serious it is; the message usually names the file at fault.
[Fri Nov 14 10:14:05.418233 2026] [proxy_fcgi:error] [pid 2817] [client 203.0.113.9:51544] AH01071: Got error 'PHP message: PHP Fatal error: Uncaught Error: Call to undefined function wc_get_product() in /home/shop/public_html/wp-content/themes/store/functions.php:212'
That line shows a PHP-FPM site (hence proxy_fcgi) throwing a fatal error on line 212 of the theme's functions.php, which the visitor saw as a 500 page. With the older mod_php the module tag reads [php:error] or similar instead.
The LogLevel directive filters what is written. The default is warn, so routine "File does not exist" 404 notices, logged at info, never appear in the error log; count 404s from the access log instead.
Apache log commands that answer real questions
A handful of tail, awk and grep one-liners answer most log questions in seconds on any VPS. In the combined format, field 1 is the IP, field 7 the path and field 9 the status. Swap the path for /var/log/httpd/access_log on RHEL-family systems.
# watch errors live while you reproduce a fault
sudo tail -f /var/log/apache2/error.log
# how many of each status code
awk '{print $9}' /var/log/apache2/access.log | sort | uniq -c | sort -rn
# top 20 paths returning 404
awk '$9 == 404 {print $7}' /var/log/apache2/access.log | sort | uniq -c | sort -rn | head -20
# busiest client IPs
awk '{print $1}' /var/log/apache2/access.log | sort | uniq -c | sort -rn | head -20
# busiest user agents (bots stand out quickly)
awk -F'"' '{print $6}' /var/log/apache2/access.log | sort | uniq -c | sort -rn | head -15
# search rotated, compressed logs too
zgrep ' 500 ' /var/log/apache2/access.log*
For a 500 error, take the time and IP from the access line and search the error log for the same second; the matching entry names the script. Repeated 403 lines usually mean a permission or .htaccess deny rule, explained in the 403 Forbidden fix guide. Thousands of POSTs to /wp-login.php or /xmlrpc.php from a few IPs are brute-force attempts worth blocking with Fail2ban.
Apache log rotation and how long logs are kept
Logs are rotated by logrotate or Apache's own rotatelogs, and retention is whatever that configuration says, not a fixed Apache rule. A combined-format line is roughly 150–300 bytes, so a site serving 1,00,000 requests a day adds about 15–30 MB of access log daily.
- logrotate (Debian/Ubuntu default):
/etc/logrotate.d/apache2rotates daily, keeps 14 compressed copies and reloads Apache afterwards so it reopens the files. - rotatelogs (piped logging):
CustomLog "|/usr/bin/rotatelogs /var/log/apache2/access.%Y-%m-%d.log 86400" combinedstarts a new file every 24 hours with no reload. The binary lives in/usr/sbinon RHEL-family systems. - cPanel: the host manages rotation; enable "Archive logs" in Raw Access if you want monthly copies kept in
~/logs/.
Tools that turn Apache logs into reports
Use cPanel's Metrics tools when you have no shell, and GoAccess when you have SSH and want a dashboard rather than raw lines.
- cPanel Metrics: Visitors (last 1,000 hits per domain), Errors, Raw Access download and AWStats, all included with WebJi® Linux hosting.
- GoAccess:
goaccess /var/log/apache2/access.log --log-format=COMBINED -o report.htmlbuilds a single HTML report; add--real-time-htmlfor a live view. - Plain shell: the one-liners above, which work on any host with SSH and need nothing installed.
If you are weighing Apache against an event-driven server for a busy site, Apache vs Nginx compares how each handles load and logging.
Frequently asked questions
Why does every visitor in my access log show the same IP address?
Apache is behind a proxy such as Cloudflare or Nginx, so %h records the proxy. Enable mod_remoteip with RemoteIPHeader X-Forwarded-For (or CF-Connecting-IP) and list the proxy as trusted, and the real client IP appears again.
Can I safely delete a large Apache log file?
Truncate it instead: sudo truncate -s 0 /var/log/apache2/access.log. Deleting with rm leaves Apache writing to the removed file, so the disk space is not freed until a reload. Fix rotation afterwards so the file does not grow back.
How do I make Apache log more detail for one module?
Apache 2.4 accepts per-module levels, for example LogLevel warn rewrite:trace3 to trace mod_rewrite decisions. Reload, reproduce the problem, then set it back, because trace levels write many lines per request.
Do access logs record the time a page took to load?
Not in the combined format. Add %D (microseconds) or %{ms}T (milliseconds) to a custom LogFormat and slow URLs become easy to sort and find.
Checked 14 November 2026 against the Apache HTTP Server 2.4 documentation · Reviewed by the WebJi® team, Jaipur.
Ready to get started with WebJi®?
Join 12,000+ businesses across India and overseas. INR pricing, 18% GST shown before payment and 24/7 email support in Hindi and English — average first reply under 60 minutes.