How to troubleshoot common Apache log errors

How to troubleshoot common Apache log errors

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 (apache2 or httpd)
  • 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:

  1. Confirm the file actually exists at the expected path: ls -la /var/www/html/old-page.html
  2. Check for typos in the URL or broken links elsewhere on the site.
  3. 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:

  1. Check the exact error message in error.log — PHP errors are usually logged right below or in a separate php_errors.log.
  2. Temporarily rename .htaccess to rule it out: mv .htaccess .htaccess.bak
  3. Check PHP-FPM or mod_php logs separately if using PHP: sudo tail -f /var/log/php8.1-fpm.log
  4. 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:

  1. Check file and directory permissions: ls -la /var/www/html/private/ Apache typically needs at least 755 on directories and 644 on files.
  2. Check ownership matches the Apache user: sudo chown -R www-data:www-data /var/www/html/private/
  3. Review the relevant block in your Apache config for Require all denied or old-style Order deny,allow directives.

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:

  1. Check whether the backend service itself is slow or hung — I test it directly, bypassing Apache, using curl against the backend port.
  2. Increase the timeout temporarily to see if it’s simply a matter of a slow (but eventually successful) response: Timeout 120ProxyTimeout 120
  3. 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:

  1. Verify each virtual host’s ServerName matches the certificate’s Common Name/SAN entries.
  2. Check for stale DNS entries pointing to the wrong IP.
  3. If using a reverse proxy in front of Apache, ensure it forwards the original Host header correctly.

General Troubleshooting Workflow I Follow

  1. Reproduce the issue and immediately check error.log and access.log timestamps around that moment.
  2. Grep for the specific status code if it’s an HTTP error: grep " 500 " /var/log/apache2/access.log | tail -20
  3. Check the syntax of your config before restarting: sudo apache2ctl configtest
  4. Increase log verbosity temporarily using LogLevel debug in the virtual host if the default level isn’t giving enough detail.
  5. 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_errors off and rely on logs instead.
  • Regularly review error.log for 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 LogLevel to warn in production instead of debug — verbose logging adds overhead on high-traffic servers.
  • Use mod_deflate or mod_brotli to 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.

References

Total
0
Shares

Leave a Reply

Previous Post
How to set up real-time monitoring for Apache with Nagios

How to Set Up Real-Time Monitoring for Apache with Nagios

Next Post
How to use logrotate to manage Apache logs

How to Use Logrotate to Manage Apache Logs

Related Posts