Everything you need to find, read, filter, and understand who is accessing your Apache web server — covering both the built-in macOS Apache and Homebrew Apache.
The location of the access log depends on whether you are using the macOS built-in Apache or Homebrew's Apache.
Access log:
/private/var/log/apache2/access_log
Error log:
/private/var/log/apache2/error_log
Symlink also works:
/var/log/apache2/access_log
Access log:
/opt/homebrew/var/log/httpd/access_log
Error log:
/opt/homebrew/var/log/httpd/error_log
Virtual host logs are defined per-vhost in config.
# Built-in Apache — grep the config for CustomLog grep -i "CustomLog" /etc/apache2/httpd.conf # Homebrew Apache grep -i "CustomLog" /opt/homebrew/etc/httpd/httpd.conf
These are the essential Terminal commands for viewing log content in real time or reviewing history.
# Built-in Apache — watch every hit as it comes in tail -f /private/var/log/apache2/access_log # Homebrew Apache tail -f /opt/homebrew/var/log/httpd/access_log # Show last 100 lines then follow tail -n 100 -f /private/var/log/apache2/access_log
# Last 50 lines tail -n 50 /private/var/log/apache2/access_log # First 20 lines (oldest entries) head -n 20 /private/var/log/apache2/access_log # Page through the whole file less /private/var/log/apache2/access_log # In less: press G to jump to end, g to go to start, q to quit
wc -l /private/var/log/apache2/access_log # Each line = one request
Use grep and awk to zero in on specific visitors, pages, errors, or time ranges without any extra tools.
grep "192.168.1.42" /private/var/log/apache2/access_log # Count how many times that IP visited grep -c "192.168.1.42" /private/var/log/apache2/access_log
grep "GET /contact" /private/var/log/apache2/access_log # Any request containing /admin grep "/admin" /private/var/log/apache2/access_log
grep " 404 " /private/var/log/apache2/access_log # All 5xx server errors grep -E " 5[0-9]{2} " /private/var/log/apache2/access_log # All 4xx client errors grep -E " 4[0-9]{2} " /private/var/log/apache2/access_log
# All entries on May 7th 2026 grep "07/May/2026" /private/var/log/apache2/access_log # Between 2pm and 3pm grep "07/May/2026:14:" /private/var/log/apache2/access_log # This month grep "May/2026" /private/var/log/apache2/access_log
# All requests from iPhones grep -i "iphone" /private/var/log/apache2/access_log # All requests from bots/crawlers grep -iE "bot|crawler|spider" /private/var/log/apache2/access_log # Exclude bots — show only real visitors grep -ivE "bot|crawler|spider" /private/var/log/apache2/access_log
# All 404 errors from a specific IP grep "192.168.1.42" /private/var/log/apache2/access_log | grep " 404 " # Errors on a specific date grep "07/May/2026" /private/var/log/apache2/access_log | grep -E " [45][0-9]{2} "
These one-liners extract useful intelligence directly from the log file — no extra software needed.
awk '{print $1}' /private/var/log/apache2/access_log \ | sort | uniq -c | sort -rn | head -10
awk '{print $7}' /private/var/log/apache2/access_log \ | sort | uniq -c | sort -rn | head -10
awk '{print $9}' /private/var/log/apache2/access_log \ | sort | uniq -c | sort -rn
awk '{print $11}' /private/var/log/apache2/access_log \ | sort | uniq -c | sort -rn | head -10
awk '{print $1}' /private/var/log/apache2/access_log \ | sort -u | wc -l
awk '{sum += $10} END {print sum " bytes / " sum/1024/1024 " MB"}' \ /private/var/log/apache2/access_log
awk '{print substr($4,14,2)":00"}' /private/var/log/apache2/access_log \ | sort | uniq -c | sort -k2
The status code (field 9 in the log) tells you what happened with each request. Here are the ones you'll see most often:
You can control exactly what gets logged, in what format, and where, by editing your Apache config file.
Open your config and confirm these two module lines are uncommented:
# Built-in Apache paths: LoadModule log_config_module libexec/apache2/mod_log_config.so LoadModule logio_module libexec/apache2/mod_logio.so # Homebrew Apache paths: LoadModule log_config_module lib/httpd/modules/mod_log_config.so LoadModule logio_module lib/httpd/modules/mod_logio.so
The combined format includes IP, timestamp, request, status, size, referrer, and user agent — this is the default and the most useful.
# "combined" — most informative (recommended) LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined # "common" — minimal (no referrer or user agent) LogFormat "%h %l %u %t \"%r\" %>s %b" common # Set which format to use and where to write: CustomLog "/private/var/log/apache2/access_log" combined
Add %D (microseconds) to log how long each request took — useful for performance debugging:
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %D" combined_time CustomLog "/private/var/log/apache2/access_log" combined_time
If you have virtual hosts, give each one its own log file inside the <VirtualHost> block:
<VirtualHost *:80> ServerName myapp.test DocumentRoot "/Users/YOUR_USERNAME/Sites/myapp" # Separate access and error log per site CustomLog "/private/var/log/apache2/myapp-access.log" combined ErrorLog "/private/var/log/apache2/myapp-error.log" </VirtualHost>
| Token | What it logs |
|---|---|
%h | Client IP address (or hostname if HostnameLookups On) |
%l | RFC 1413 identity (almost always -) |
%u | Authenticated username (- if no auth) |
%t | Time the request was received |
%r | First line of request (method + path + protocol) |
%>s | Final HTTP status code sent to client |
%b | Bytes sent (excluding headers), - if zero |
%{Referer}i | Referer header — where the visitor came from |
%{User-Agent}i | User-Agent header — browser and OS string |
%D | Request time in microseconds (performance) |
%T | Request time in seconds |
%v | ServerName of virtual host serving the request |
# Test config first sudo apachectl configtest # Then graceful restart sudo apachectl graceful
After changing the log format, only new requests will use the new format. Existing log entries keep the old format. Consider rotating the log after a format change to keep things consistent.
Access logs grow without limit unless you rotate them. macOS uses newsyslog to rotate system logs automatically — Apache's built-in logs are included by default.
# Check size of Apache log files ls -lh /private/var/log/apache2/ # Homebrew ls -lh /opt/homebrew/var/log/httpd/
# Archive the current log with a timestamp sudo mv /private/var/log/apache2/access_log \ /private/var/log/apache2/access_log.$(date +%Y%m%d) # Graceful restart so Apache creates a fresh log file sudo apachectl graceful
Never just delete an active log file while Apache is running — it will keep writing to the deleted file descriptor. Always move or rename it, then do a graceful restart so Apache opens a new file.
If you want to turn off access logging completely, comment out the CustomLog line in httpd.conf:
# Add a # to disable:
#CustomLog "/private/var/log/apache2/access_log" combined