How to Troubleshoot Common Apache Log Errors
Every time something goes wrong on a web server I manage, the first thing I do is open the Apache logs. Over the years I’ve learned to recognize the patterns behind the most common errors, and in this post I’ll share exactly how I diagnose and fix them.
Why Apache Logs Matter
Apache produces two primary log files that tell you almost everything you need to know when something breaks:
- access.log — records every request made to the server (IP, URL, status code, user agent, etc.)
- error.log — records server-side errors, warnings, and notices
If a page won’t load, a script is crashing, or performance has tanked, these two files are where I start.
Prerequisites
- Apache installed and running (
apache2orhttpd) - Access to the log directory, typically
/var/log/apache2/(Debian/Ubuntu) or/var/log/httpd/(RHEL/CentOS) - Basic command-line familiarity (
grep,tail,less)
Step 1: Know Where Your Logs Live
# Debian/Ubuntu
/var/log/apache2/access.log
/var/log/apache2/error.log
# CentOS/RHEL
/var/log/httpd/access_log
/var/log/httpd/error_log
If you’re not sure, check your virtual host configuration for ErrorLog and CustomLog directives, or run:
sudo apache2ctl -S
Step 2: Watch Logs in Real Time
When actively debugging, I keep a terminal open tailing the error log:
sudo tail -f /var/log/apache2/error.log
Then I reproduce the issue in a browser and watch what gets logged in real time.
Common Error #1: 404 Not Found
What it looks like in access.log:
192.168.1.10 - - [26/Jul/2026:10:15:32 +0000] "GET /old-page. html HTTP/ 1.1" 404 217
Cause: The requested file or route doesn’t exist on the server.
How I troubleshoot it:
- Confirm the file actually exists at the expected path:
ls -la /var/www/html/old-page.html - Check for typos in the URL or broken links elsewhere on the site.
- If this was a valid page that moved, set up a redirect:
Redirect 301 /old-page.html /new-page.html
Common Error #2: 500 Internal Server Error
What it looks like in error.log:
[Sun Jul 26 10:20:11.234567 2026] [core:error] [pid 4521] [client 192.168.1.10:52344] End of script output before headers: index.php
Cause: Usually a script (PHP, CGI) crashed before producing output, or there’s a misconfigured .htaccess file.
How I troubleshoot it:
- Check the exact error message in
error.log— PHP errors are usually logged right below or in a separatephp_errors.log. - Temporarily rename
.htaccessto rule it out:mv .htaccess .htaccess.bak - Check PHP-FPM or mod_php logs separately if using PHP:
sudo tail -f /var/log/php8.1-fpm.log - Enable more verbose PHP error reporting temporarily in
php.ini:display_errors = Onerror_reporting = E_ALL
Common Error #3: 403 Forbidden
What it looks like in error.log:
[Sun Jul 26 10:22:45.123456 2026] [core:error] [pid 4522] [client 192.168.1.10:52350] AH01630: client denied by server configuration: /var/www/html/private/
Cause: Permission issues, missing index file, or an explicit Deny directive.
How I troubleshoot it:
- Check file and directory permissions:
ls -la /var/www/html/private/Apache typically needs at least755on directories and644on files. - Check ownership matches the Apache user:
sudo chown -R www-data:www-data /var/www/html/private/ - Review the relevant
block in your Apache config forRequire all deniedor old-styleOrder deny,allowdirectives.
Common Error #4: “Could Not Reliably Determine the Server’s Fully Qualified Domain Name”
What it looks like:
apache2: Could not reliably determine the server's fully qualified domain name, using 127.0.1.1. Set the 'ServerName' directive globally to suppress this message
Cause: Apache’s global config is missing a ServerName directive.
How I fix it:
echo "ServerName localhost" | sudo tee /etc/apache2/conf-available/servername.conf
sudo a2enconf servername
sudo systemctl restart apache2
Common Error #5: “Address Already in Use: AH00072: make_sock: could not bind to address”
What it looks like:
(98)Address already in use: AH00072: make_sock: could not bind to address [::]:80
Cause: Another process (often Nginx or a previous Apache instance) is already using port 80.
How I troubleshoot it:
sudo lsof -i :80
sudo netstat -tulpn | grep :80
Then either stop the conflicting service or change Apache’s Listen directive to a different port.
Common Error #6: Permission Denied on Log Files
What it looks like:
(13)Permission denied: AH00091: httpd: could not open error log file /var/log/apache2/error.log.
Cause: Incorrect ownership or SELinux restrictions.
How I fix it:
sudo chown root:adm /var/log/apache2/error.log
sudo chmod 640 /var/log/apache2/error.log
On SELinux-enabled systems (RHEL/CentOS), also check:
sudo restorecon -Rv /var/log/httpd/
Common Error #7: MaxRequestWorkers Reached
What it looks like:
[mpm_prefork:error] AH00161: server reached MaxRequestWorkers setting, consider raising the MaxRequestWorkers setting
Cause: Too many simultaneous connections for your configured MPM limits.
How I fix it:
I adjust the MPM configuration (usually in /etc/apache2/mods-available/mpm_prefork.conf):
StartServers 5
MinSpareServers 5
MaxSpareServers 10
MaxRequestWorkers 250
MaxConnectionsPerChild 0
Then restart Apache and monitor memory usage, since raising this too high can exhaust RAM.
Common Error #8: Timeout Errors
What it looks like in error.log:
[Sun Jul 26 10:35:12.456789 2026] [core:info] [pid 4530] [client 192.168.1.10:52360] AH00992: (70007)The timeout specified has expired: [client 192.168.1.10:52360] AH01075: Error dispatching request to
Cause: A backend process (like a proxied application server) took too long to respond, exceeding Apache’s Timeout or ProxyTimeout setting.
How I troubleshoot it:
- Check whether the backend service itself is slow or hung — I test it directly, bypassing Apache, using
curlagainst the backend port. - Increase the timeout temporarily to see if it’s simply a matter of a slow (but eventually successful) response:
Timeout 120ProxyTimeout 120 - Investigate the root cause of backend slowness — often a database query, an external API call, or resource exhaustion on the backend server.
Common Error #9: SSL Handshake Failures
What it looks like in error.log:
[Sun Jul 26 10:40:03.112233 2026] [ssl:error] [pid 4535] AH02032: Hostname mydomain.local provided via SNI and hostname provided via HTTP are different
Cause: A mismatch between the SNI (Server Name Indication) hostname and the Host header sent by the client, often seen with misconfigured virtual hosts or certain proxy setups.
How I troubleshoot it:
- Verify each virtual host’s
ServerNamematches the certificate’s Common Name/SAN entries. - Check for stale DNS entries pointing to the wrong IP.
- If using a reverse proxy in front of Apache, ensure it forwards the original
Hostheader correctly.
General Troubleshooting Workflow I Follow
- Reproduce the issue and immediately check
error.logandaccess.logtimestamps around that moment. - Grep for the specific status code if it’s an HTTP error:
grep " 500 " /var/log/apache2/access.log | tail -20 - Check the syntax of your config before restarting:
sudo apache2ctl configtest - Increase log verbosity temporarily using
LogLevel debugin the virtual host if the default level isn’t giving enough detail. - Restart, not just reload, if module changes were made:
sudo systemctl restart apache2
Using Log Analysis Tools to Spot Patterns Faster
Manually grepping through logs works fine for a single incident, but when I’m trying to spot recurring patterns over days or weeks, I lean on tools like AWStats or Logwatch (both of which I’ve covered in separate posts) to summarize error trends automatically. A sudden jump in 404s reported in a daily Logwatch email, for instance, is often my first hint that something changed — a broken deploy, a removed page still linked from elsewhere, or a bot scanning for vulnerable paths.
I also keep a small cheat-sheet of grep patterns I reach for constantly:
# Count occurrences of each status code today
awk '{print $9}' /var/log/apache2/access.log | sort | uniq -c | sort -rn
# Find the top 10 IPs generating errors
grep " 500 \| 502 \| 503 " /var/log/apache2/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -10
# Search error log for a specific time window
sed -n '/10:00:00/,/11:00:00/p' /var/log/apache2/error.log
These one-liners have saved me countless hours compared to scrolling through logs manually, especially during an active incident when time matters most.
Security Best Practices
- Never expose detailed error messages (like PHP stack traces) to end users in production — keep
display_errorsoff and rely on logs instead. - Regularly review
error.logfor repeated 403/404 patterns from the same IP, which can indicate scanning or brute-force attempts. - Restrict log file permissions so only root and the appropriate group can read them.
Performance Optimization Tips
- Set
LogLeveltowarnin production instead ofdebug— verbose logging adds overhead on high-traffic servers. - Use
mod_deflateormod_brotlito compress large log-heavy responses (not the logs themselves, but the pages the errors point to). - Periodically archive and rotate logs (see my logrotate guide) so grepping through them stays fast.
FAQs
Where do I find PHP-specific errors if they don’t show in Apache’s error.log? Check your php.ini for the error_log directive — many setups log PHP errors to a separate file like /var/log/php_errors.log.
What does “AH” mean in Apache error codes? It stands for “Apache HTTPD” and is simply Apache’s internal error code prefix, useful for searching official documentation.
How do I make error logs more detailed for debugging? Temporarily set LogLevel debug in your virtual host block, then revert to warn once you’re done.
Can I search multiple log files at once? Yes: grep "500" /var/log/apache2/*.log searches across all logs in that directory.
Summary and Key Takeaways
Most Apache issues leave a clear trail in access.log and error.log — the trick is knowing what to look for. Whether it’s a 404, a 500, a permissions issue, or a port conflict, the error message almost always tells you exactly where to look next. I always start troubleshooting by tailing the logs live, reproducing the issue, and working outward from there.