I’ve spent years tuning Apache installations for clients ranging from small blogs to high-traffic e-commerce stores, and I can tell you the default configuration that ships with most Linux distributions is almost never optimal for production. In this post, I’ll share the exact process I go through whenever I’m asked to speed up a slow Apache server.
Why Apache Performance Tuning Matters
A poorly tuned Apache server wastes memory, chokes under concurrent traffic, and delivers a worse experience to every visitor. I’ve seen servers with 16GB of RAM crawl under moderate load simply because the default MPM settings were left untouched. Tuning isn’t optional if you’re serving real traffic — it’s the difference between a site that scales and one that falls over during a traffic spike.
Prerequisites
- SSH access with root or sudo privileges
- Apache 2.4+ installed
- Basic understanding of your server’s available RAM and CPU cores
- A staging environment to test changes before pushing to production (I always insist on this)
Step 1: Choose the Right MPM (Multi-Processing Module)
Apache can run using different MPMs, and this is the single most impactful decision you’ll make.
- prefork — one process per connection, no threading. Safe for non-thread-safe modules like older mod_php, but memory-hungry.
- worker — hybrid process/thread model, more efficient than prefork.
- event — the modern default, designed to handle keep-alive connections more efficiently than worker.
I check which MPM is active with:
apachectl -V | grep -i mpm
If you’re still running PHP as an Apache module (mod_php), you’re stuck with prefork. My strong recommendation is to switch to PHP-FPM and use the event MPM instead — this alone has cut memory usage in half on several servers I’ve managed.
Step 2: Tune MPM Worker/Event Settings
Once you’re on the event MPM, the defaults are usually too conservative or too aggressive depending on your traffic. Here’s a configuration I typically start with for a mid-sized VPS with 4GB RAM:
<IfModule mpm_event_module>
StartServers 2
MinSpareThreads 25
MaxSpareThreads 75
ThreadLimit 64
ThreadsPerChild 25
MaxRequestWorkers 150
MaxConnectionsPerChild 10000
</IfModule>
The key value to calculate is MaxRequestWorkers. I use this rough formula:
MaxRequestWorkers = Total RAM available for Apache / Average memory per process
If each Apache process uses around 20MB and you have 3GB available for Apache, that gives roughly 150 workers.
Step 3: Disable Unnecessary Modules
Every loaded module adds a small amount of overhead. I always run:
apache2ctl -M
And then disable anything not actually in use:
sudo a2dismod autoindex
sudo a2dismod status
sudo systemctl restart apache2
Common modules I disable on production servers unless specifically needed: autoindex, userdir, status (or at least restrict it), and cgi if you’re not running CGI scripts.
Step 4: Enable Compression and Caching
I cover gzip compression in detail in a separate post, but it’s worth repeating here: enabling mod_deflate and setting proper cache headers with mod_expires are two of the highest-impact, lowest-effort changes you can make.
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/jpg "access plus 1 year"
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType text/css "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month"
</IfModule>
Step 5: Enable KeepAlive Correctly
KeepAlive lets a single TCP connection serve multiple requests, which reduces the overhead of repeated handshakes. I always make sure it’s on, but with a sane timeout:
KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 5
A KeepAliveTimeout that’s too long (I’ve seen defaults of 15 seconds) ties up worker threads unnecessarily. I bring it down to 2-5 seconds on high-traffic sites.
Step 6: Use a PHP Opcode Cache
If your site runs PHP, an opcode cache like OPcache is non-negotiable in my book. It caches compiled PHP bytecode so scripts don’t need to be recompiled on every request.
opcache.enable=1
opcache.memory_consumption=256
opcache.max_accelerated_files=20000
opcache.revalidate_freq=60
I add these to /etc/php/8.x/apache2/conf.d/10-opcache.ini or the equivalent for your PHP version.
Step 7: Move to PHP-FPM
If you’re still running mod_php, this is the change I recommend most. PHP-FPM runs PHP as a separate process pool, freeing Apache to use the more efficient event MPM. I install it alongside libapache2-mod-fcgid or use mod_proxy_fcgi:
sudo apt install php-fpm libapache2-mod-proxy-fcgi
sudo a2enmod proxy_fcgi setenvif
sudo a2enconf php8.1-fpm
sudo systemctl restart apache2
Step 8: Enable HTTP/2
HTTP/2 multiplexes multiple requests over a single connection, dramatically reducing latency for pages with many assets. It requires SSL to be enabled (which you should have anyway):
Protocols h2 http/1.1
I add this inside the SSL virtual host block and confirm it’s working with browser dev tools or curl -I --http2 https://yourdomain.com.
Step 9: Monitor and Benchmark
Tuning without measuring is just guessing. My go-to tools:
- Apache Bench (ab) for quick load tests:
ab -n 1000 -c 50 https://yourdomain.com/ - htop to watch CPU and memory in real time during load tests
- mod_status (restricted to internal IPs only) to see live worker activity
<Location "/server-status">
SetHandler server-status
Require ip 127.0.0.1
</Location>
Real-World Use Case
On one WordPress site pulling around 500,000 monthly visits, switching from mod_php/prefork to PHP-FPM/event, adding OPcache, and tuning MaxRequestWorkers reduced average response time from 1.4 seconds to under 300ms, and the server handled traffic spikes during sales events without a single 502 error.
Common Mistakes to Avoid
- Setting
MaxRequestWorkerstoo high and letting the server run out of memory under load. - Leaving
KeepAliveTimeoutat long defaults, which wastes worker threads. - Forgetting to disable modules you don’t use.
- Skipping benchmarks — always test before and after any change.
Troubleshooting Tips
If your server starts throwing “server reached MaxRequestWorkers setting” warnings in the error log, that’s a clear signal you’re either under-provisioned or need to increase the limit (if you have RAM headroom). If Apache is consuming too much memory, check for memory leaks in custom modules or scripts, and confirm you’re not still running mod_php alongside FPM by mistake.
Security and Performance Best Practices
- Always test configuration changes with
apachectl configtestbefore restarting. - Keep Apache and all modules updated to the latest stable version.
- Combine server-level tuning with application-level caching (like Redis or Memcached) for the biggest gains.
- Restrict
mod_statusto internal networks only — it can leak information if exposed publicly.
Frequently Asked Questions
Which MPM should I use in 2026? For most modern setups with PHP-FPM, the event MPM is the best choice. Only use prefork if you’re stuck with legacy mod_php.
How do I know if my server is under-resourced? Watch for MaxRequestWorkers warnings in your error logs, sustained high CPU/memory usage, or slow response times under moderate concurrent load.
Does enabling HTTP/2 require code changes? No, it’s purely a server-level configuration change, though it works best when combined with a valid SSL certificate.
Is caching more important than server tuning? They work together. Application and browser caching reduce the number of requests hitting Apache at all, while server tuning ensures Apache handles the requests that do arrive efficiently.
Summary and Key Takeaways
Optimizing Apache is a layered process: choose the right MPM, tune worker/thread limits to match your hardware, enable compression and caching, move to PHP-FPM if applicable, and always benchmark your changes. None of these steps alone will transform a slow server, but together they compound into a genuinely fast, resilient setup.