Performance Tuning, Logging & Troubleshooting

Apache In Depth

Chapter 9 · Performance Tuning, Logging & Troubleshooting

Web Servers Fundamentals Chapter 9 named KeepAlive/KeepAliveTimeout and mod_cache without real values or directives, and closed with a serious, correct warning: caching personalized content by URL alone can leak one user's data to another. This chapter goes deep on all three — KeepAlive tuning tied directly back to Chapter 2's own capacity math, the actual mod_cache_disk mechanism that prevents exactly the leak Fundamentals warned about, and the practical logging and troubleshooting tools that round out this course before its capstone.

KeepAlive Tuning — Tied to Chapter 2's Own Capacity Math

KeepAlive On KeepAliveTimeout 5 MaxKeepAliveRequests 100

KeepAliveTimeout isn't a free setting to raise generously — it directly connects to Chapter 2's own MaxRequestWorkers capacity math. Under Prefork, a connection sitting idle during its keep-alive window still occupies an entire process; under Worker, it still occupies an entire thread — either way, it counts against the same real ceiling a genuinely busy request would, for as long as the timeout keeps it open. A KeepAliveTimeout set too high (a common instinct: "longer must be better for reuse") can quietly starve real capacity with connections doing nothing at all. This is exactly the problem Chapter 2's own Event MPM solves — its AsyncRequestWorkerFactor handling of idle keep-alive connections is precisely what makes a longer KeepAliveTimeout far cheaper under Event than under Prefork or Worker. A short window (5–15 seconds is typical) is the usual safe default outside of Event.

mod_cache_disk In Depth — Actually Preventing Fundamentals' Own Warning

CacheRoot /var/cache/apache2/mod_cache_disk CacheEnable disk /static/ CacheDirLevels 2 CacheDirLength 1

CacheRoot sets where cached responses are stored on disk; CacheEnable disk /static/ turns caching on for that path specifically, rather than site-wide; CacheDirLevels/CacheDirLength control how cache files are spread across subdirectories to avoid one directory holding an unmanageable number of files. The part that actually matters for Fundamentals' own warning is what mod_cache does by default: it respects Cache-Control headers from the backend, refusing to cache anything marked private or no-store. For content that legitimately does vary per user but is still worth caching, the backend's own Vary response header — e.g. Vary: Cookie — tells mod_cache to key its stored copies separately per distinct value of that header, rather than serving one shared cached copy to every visitor of the same URL.

A correctly configured Vary header can still mean "no real cache hit"
Vary: Cookie genuinely prevents the cross-user leak Fundamentals warned about — but on a page where every logged-in user has a unique session cookie, keying the cache by that header means each user effectively gets their own single-visitor cache bucket, which almost never produces a real hit on a second request. Configuring Vary correctly can be a technically correct fix that still delivers zero real performance benefit. For a genuinely personalized page like an account dashboard, the honest fix is usually not "cache it correctly" at all — it's respecting the backend's own Cache-Control: private and simply not caching that response at the server/proxy layer in the first place.

Custom Log Formats

LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined CustomLog /var/log/apache2/access.log combined

Fundamentals mentioned LogFormat exists without ever showing it; the standard combined format above is worth being able to read directly: %h is the client IP, %l is the (almost always unused) identd field, %u the authenticated username if any, %t the timestamp, %r the full request line, %>s the final HTTP status code, %b the response size in bytes, and the two %{...}i tokens pull specific request headers directly — Referer and User-Agent here, but any header name works the same way.

Troubleshooting: configtest Before Every Reload

apachectl configtest # or, equivalently apachectl -t

This validates the entire configuration's syntax without actually reloading or restarting anything — critical to run before every real reload, since applying a broken config live can take the whole site down with it. Two of the most common real error-log patterns worth recognizing on sight: AH00526: Syntax error on line N (a straightforward config typo, pointing at the exact line), and (98)Address already in use: AH00072: make_sock: could not bind to address (something else — often a previous Apache process that didn't fully stop — is already holding the port). A 403 logged as AH01630: client denied by server configuration traces directly back to Chapter 8's own Require/RequireAll rules rejecting the request.

ToolChecksIntroduced
apachectl configtest / -tConfig syntax validity, before applying itThis chapter
mod_statusLive worker/thread busy-idle stateChapter 2
apachectl -SParsed VirtualHost matching orderChapter 5
balancer-managerLive backend pool healthChapter 7
The fourth and final instance of this course's own recurring lesson
apachectl configtest completes a pattern this course has now shown four separate times: mod_status for live capacity, apachectl -S for real routing order, balancer-manager for live pool health, and now configtest for config validity before it's even applied. Every one of them exists for the same underlying reason — check what Apache is actually doing, or about to do, rather than trusting what the config file says or assumes.

Hands-On Exercises

Exercise 1

A server runs Prefork with MaxRequestWorkers set to 100, and a team sets KeepAliveTimeout to 300 seconds thinking longer is simply better for connection reuse. Explain, using this chapter's own material and Chapter 2's capacity math, why this could genuinely reduce the server's real usable capacity under real traffic.

📄 View solution
Exercise 2

A team configures Vary: Cookie on their logged-in account dashboard specifically to make caching it "safe," then is confused when their cache hit rate for that page stays at almost 0%. Explain what's happening, and what the more honest fix usually is for a page like this.

📄 View solution
Exercise 3

A team edits their Apache config and immediately runs a full restart, and the entire site goes down with no traffic served at all. Using this chapter's own material, what single command should they have run first, and what would it likely have told them before they ever restarted the live server?

📄 View solution

Chapter 9 Quick Reference

  • KeepAliveTimeout — too high genuinely costs real capacity under Prefork/Worker (each idle connection still holds a process/thread); Event handles this far more cheaply
  • mod_cache_disk — respects Cache-Control by default; Vary keys the cache per-header value to avoid cross-user leaks, but can still yield near-zero real hit rate on highly personalized pages
  • LogFormat combined%h IP, %u user, %t time, %r request line, %>s status, %b size, %{Header}i for any request header
  • apachectl configtest (-t) — validate syntax before every reload; never reload/restart on an unchecked config
  • The fourth live-introspection tool in this course, after mod_status, apachectl -S, and balancer-manager