Apache Performance Tuning: Complete Guide

· James Thornton · 10 min read

Advanced

Apache HTTP Server has been the backbone of the web since 1995, and despite Nginx overtaking it in market share, Apache remains the default choice for millions of websites — particularly those running PHP, WordPress, and shared hosting environments. Proper performance tuning transforms Apache from a resource-heavy default installation into a high-performance server capable of handling thousands of concurrent connections with modern MPM (Multi-Processing Module) configurations.

The key to Apache performance is choosing the right MPM and tuning it for your workload. The Event MPM, combined with PHP-FPM and proper module management, brings Apache's performance much closer to Nginx than many assume.

How Do Apache MPMs Affect Performance?

Apache's Multi-Processing Modules determine how the server handles concurrent connections. The choice of MPM is the single most impactful performance decision.

Prefork MPM creates a separate process for each connection. Each process handles one request at a time. This is the safest MPM (complete isolation between requests) but the most resource-intensive. Each process consumes 10-50MB of RAM, limiting concurrency. Prefork is required for non-thread-safe PHP extensions but should be avoided when possible.

Worker MPM uses multi-threaded processes. Each process spawns multiple threads, each handling one connection. Memory usage is much lower than Prefork because threads share process memory. However, all modules must be thread-safe.

Event MPM (recommended) extends Worker with an event-driven model for keepalive connections. Idle keepalive connections are handled by dedicated listener threads instead of blocking worker threads. This dramatically improves concurrency for workloads with many persistent connections — similar to Nginx's event-driven architecture.

Apache MPM comparison showing Prefork, Worker, and Event processing models with memory usage and concurrency characteristics Apache MPM Comparison Prefork MPM 1 process per connection 10-50MB per process Max ~256 concurrent Non-thread-safe PHP only Worker MPM Multi-threaded processes Shared memory (lower RAM) Max ~1000 concurrent Thread-safe modules required Event MPM Event-driven keepalive Best memory efficiency Max ~10000 concurrent Recommended for production Recommendation: Event MPM + PHP-FPM for best performance

How Do You Tune Event MPM Configuration?

# /etc/apache2/mods-enabled/mpm_event.conf
<IfModule mpm_event_module>
    ServerLimit           16
    StartServers          4
    MinSpareThreads       75
    MaxSpareThreads       250
    ThreadsPerChild       25
    MaxRequestWorkers     400
    MaxConnectionsPerChild 10000
</IfModule>

MaxRequestWorkers (formerly MaxClients) is the most critical setting — it defines the maximum number of simultaneous connections. Set this based on available RAM: calculate available RAM for Apache, divide by per-worker memory usage (check with ps aux | grep apache). Going too high causes swapping; too low wastes capacity.

Performance Comparison: Edge vs Origin Processing
Metric Edge Processing Origin Processing Improvement
Latency (p50)8–15 ms120–300 ms15–20x faster
Cache Hit Ratio85–95%N/AReduces origin load
TLS Handshake1–5 ms (nearby PoP)50–150 ms10–30x faster
Bandwidth Cost$0.01–0.04/GB$0.05–0.12/GB3–5x cheaper
Availability99.99% (multi-PoP)99.9% (single DC)10x fewer outages

MaxConnectionsPerChild restarts worker processes after handling a set number of requests. This prevents memory leaks from accumulating. Set to 10,000-50,000 for applications with potential memory leaks; set to 0 (unlimited) for well-tested applications.

Why Does .htaccess Impact Performance?

Apache checks for .htaccess files in every directory in the path for every request. A request for /var/www/html/blog/2024/post.html triggers .htaccess checks in /var, /var/www, /var/www/html, /var/www/html/blog, and /var/www/html/blog/2024 — five directory lookups per request.

For high-performance sites, move all .htaccess rules into the virtual host configuration and disable .htaccess entirely:

<Directory /var/www/html>
    AllowOverride None
</Directory>

This eliminates the per-request directory walks. On busy servers, this single change can improve throughput by 10-20%.

How Do You Configure Compression in Apache?

# mod_deflate configuration
<IfModule mod_deflate.c>
    AddOutputFilterByType DEFLATE text/plain text/css text/xml
    AddOutputFilterByType DEFLATE text/javascript application/javascript
    AddOutputFilterByType DEFLATE application/json application/xml
    AddOutputFilterByType DEFLATE image/svg+xml

    # Don't compress images, PDFs, or already-compressed files
    SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png|webp)$ no-gzip dont-vary
    SetEnvIfNoCase Request_URI \.(?:exe|t?gz|zip|bz2|sit|rar)$ no-gzip dont-vary
    SetEnvIfNoCase Request_URI \.pdf$ no-gzip dont-vary

    DeflateCompressionLevel 6
</IfModule>

# mod_expires for static asset caching
<IfModule mod_expires.c>
    ExpiresActive On
    ExpiresByType text/css "access plus 1 year"
    ExpiresByType application/javascript "access plus 1 year"
    ExpiresByType image/jpeg "access plus 1 year"
    ExpiresByType image/png "access plus 1 year"
    ExpiresByType image/svg+xml "access plus 1 year"
    ExpiresByType font/woff2 "access plus 1 year"
</IfModule>

How Should You Configure PHP with Apache?

The traditional mod_php loads PHP into every Apache worker process, consuming memory even for static file requests. PHP-FPM (FastCGI Process Manager) separates PHP processing from Apache, allowing Apache to handle static files efficiently while PHP requests are processed by a dedicated PHP process pool.

# Enable PHP-FPM with Apache Event MPM
<FilesMatch \.php$>
    SetHandler "proxy:unix:/run/php/php-fpm.sock|fcgi://localhost"
</FilesMatch>

# PHP-FPM pool configuration (/etc/php/8.3/fpm/pool.d/www.conf)
[www]
pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20
pm.max_requests = 500

How Do You Optimize KeepAlive and Logging?

# KeepAlive optimization
KeepAlive On
MaxKeepAliveRequests 500      # Increase from default 100
KeepAliveTimeout 5            # Reduce from default 5 (or lower to 2 for high traffic)

# Log optimization
# Buffer log writes (reduces disk I/O)
BufferedLogs On

# Conditional logging: skip health check and static file requests
SetEnvIf Request_URI "^/health$" dontlog
SetEnvIf Request_URI "\.(css|js|ico|png|jpg|woff2)$" dontlog
CustomLog /var/log/apache2/access.log combined env=!dontlog

Reducing KeepAliveTimeout from the default 5 seconds frees up worker threads faster. On high-traffic sites, even 2 seconds is sufficient — most page resources load within 1-2 seconds. Increasing MaxKeepAliveRequests allows a single connection to serve more resources before closing, reducing TCP handshake overhead.

Apache optimization checklist showing high-impact changes from MPM selection through module optimization Apache Performance Checklist 1. Switch to Event MPM + PHP-FPM 2. Disable .htaccess (AllowOverride None) 3. Enable mod_deflate compression 4. Set mod_expires for static assets 5. Tune MaxRequestWorkers for RAM 6. Optimize KeepAlive and logging

External References

References & Standards: Cloudflare documentation · HTTP/2 specification