Web Servers (Apache, Nginx, Caddy)

Tuning Apache and Nginx for High-Traffic Websites: A Comprehensive Guide

By the Domain India teamPublished 9 min read
Knowledge base article
Contents (16 sections)

When a website's traffic grows, the web server's defaults stop being good enough: pages slow down, memory runs out and requests start to queue. Apache and Nginx can both handle heavy traffic, but each needs a few settings sized for your server and your application. This guide covers the changes that matter most in 2026, and how to measure whether they worked.

This guide is for your own server

Everything here changes server-wide configuration, so it needs root access on a VPS or dedicated server. On Domain India shared hosting, the web server is managed for every account on the server and you can't change it. To speed up a site on shared hosting, see My website is slow.

Key takeaways

Measure before and after every change. On Apache, use the event MPM with PHP-FPM, size MaxRequestWorkers to your RAM, keep KeepAlive on with a short timeout, and enable compression and browser caching. On Nginx, set worker_processes auto, raise worker_connections and the open-file limit, cache static files and, where it suits the application, use short-lived page caching. Nginx in front of Apache or several app servers gives you caching and load balancing in one place. Beyond that, a CDN and application caching do more than any server setting.

1. Measure first

Tuning without numbers is guessing. Before you change anything, record:

  • Response time and throughput under load, with a tool such as wrk, k6 or ab run from another machine, never from the server itself.
  • Memory and CPU while the test runs: top, free -m and vmstat 1.
  • Errors in the logs: /var/log/apache2/ or /var/log/httpd/, and /var/log/nginx/.
bash
wrk -t4 -c200 -d60s https://www.example.com/

Test a page that represents real traffic, change one thing at a time, and test again. Test on a staging copy where you can.

2. Apache or Nginx?

AspectApacheNginx
ArchitectureProcesses and threads (MPM)Event-driven, a few worker processes
Per-directory config.htaccess supportedNo .htaccess; all config is central
Static filesGoodExcellent, very low memory
PHPThrough PHP-FPM (or mod_php with prefork)Through PHP-FPM only
Common roleApplication server, .htaccess compatibilityFront-end proxy, static files, load balancer

Many high-traffic setups use both: Nginx at the front for TLS, static files and caching, with Apache or application servers behind it.

3. Tuning Apache

Use the event MPM with PHP-FPM

Apache's Multi-Processing Module (MPM) decides how it handles connections. Event is the modern choice: it uses threads and handles idle keep-alive connections cheaply. Prefork runs one process per connection and exists mainly for the old mod_php, which is not thread-safe. Run PHP through PHP-FPM instead, and you can use event.

On Debian or Ubuntu:

bash
sudo a2dismod php8.3 mpm_prefork        # use the mod_php version you have, if any
sudo a2enmod mpm_event proxy_fcgi setenvif
sudo a2enconf php8.3-fpm                # match your installed PHP-FPM version
sudo apachectl configtest && sudo systemctl restart apache2

On AlmaLinux or Rocky Linux, the MPM is chosen in /etc/httpd/conf.modules.d/00-mpm.conf, and PHP-FPM is the default.

Size the workers to your memory

MaxRequestWorkers is the number of requests Apache serves at once. Too high and the server swaps; too low and visitors queue. With event, the threads are light; the real memory cost is in PHP-FPM, which you size separately (section 5).

apache
<IfModule mpm_event_module>
    StartServers             2
    ServerLimit             16
    ThreadsPerChild         25
    MaxRequestWorkers      400
    MinSpareThreads         50
    MaxSpareThreads        150
    MaxConnectionsPerChild   0
</IfModule>

MaxRequestWorkers must not exceed ServerLimit × ThreadsPerChild. These numbers are a starting point for a server with a few GB of RAM; adjust them against your measurements.

Keep-alive and timeouts

apache
KeepAlive On
MaxKeepAliveRequests 500
KeepAliveTimeout 3
Timeout 60

A short KeepAliveTimeout frees connections quickly while still letting one browser fetch a page's assets over a single connection.

Compression, caching headers and HTTP/2

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

<IfModule mod_expires.c>
    ExpiresActive On
    ExpiresByType image/webp "access plus 30 days"
    ExpiresByType text/css "access plus 7 days"
    ExpiresByType application/javascript "access plus 7 days"
</IfModule>

Protocols h2 http/1.1

Enable the modules with a2enmod deflate expires http2 on Debian and Ubuntu. HTTP/2 needs the event (or worker) MPM. mod_brotli compresses text better than deflate, if your build includes it. Disable modules you don't use, such as status on a public vhost, autoindex or cgi; but never disable ssl on an HTTPS site.

4. Tuning Nginx

Workers and connections

nginx
worker_processes auto;          # one per CPU core
worker_rlimit_nofile 65535;

events {
    worker_connections 4096;
}

Each connection uses a file descriptor, and a proxied request uses two, so raise the open-file limit with the connection count.

Keep-alive, compression and static files

nginx
http {
    sendfile on;
    tcp_nopush on;
    keepalive_timeout 15s;

    gzip on;
    gzip_comp_level 5;
    gzip_min_length 256;
    gzip_types text/plain text/css application/javascript application/json image/svg+xml;

    open_file_cache max=10000 inactive=60s;
}

Serve static assets straight from disk with long cache headers:

nginx
location ~* \.(css|js|png|jpg|jpeg|gif|webp|avif|svg|woff2)$ {
    expires 30d;
    add_header Cache-Control "public";
    access_log off;
}

For HTTP/2, Nginx 1.25.1 and later use a separate http2 on; directive in the server block; older versions put http2 on the listen 443 ssl line. Check yours with nginx -v.

Buffers and body size

Defaults suit most sites. Set client_max_body_size to the largest upload your application really needs (for example 64m), rather than the 1 MB default, and short client_header_timeout and client_body_timeout values (10–15s) to drop slow clients.

5. Size PHP-FPM, or everything else is wasted

With either web server, PHP-FPM usually limits throughput. In the pool file (for example /etc/php/8.3/fpm/pool.d/www.conf):

ini
pm = dynamic
pm.max_children = 40
pm.start_servers = 8
pm.min_spare_servers = 4
pm.max_spare_servers = 12
pm.max_requests = 500

Work out pm.max_children as the RAM you can give PHP divided by the average memory of one PHP process (check it with ps -o rss -C php-fpm8.3, or php-fpm on RHEL-family systems). Enable OPcache as well; see the PHP OPcache guide.

6. Nginx as reverse proxy, cache and load balancer

nginx
upstream app_servers {
    least_conn;
    server 10.0.0.11:8080;
    server 10.0.0.12:8080;
    keepalive 32;
}

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=pages:50m inactive=10m max_size=1g;

server {
    location / {
        proxy_pass http://app_servers;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_cache pages;
        proxy_cache_valid 200 1m;
        proxy_cache_use_stale error timeout updating;
        proxy_cache_bypass $cookie_session;
        proxy_no_cache $cookie_session;
    }
}

The balancing methods are round robin (the default), least_conn and ip_hash for sticky clients. Even a one-minute cache of public pages absorbs a traffic spike. Never cache pages for logged-in users: bypass the cache on the session cookie, as above, and check your application's cookie name. When Apache sits behind Nginx, enable mod_remoteip so Apache logs the visitor's real IP address.

Test before every reload

Run sudo apachectl configtest or sudo nginx -t before reloading. A reload with a broken configuration can take every site on the server offline. Prefer systemctl reload to restart, so open connections finish.

7. Handling traffic spikes

  • A CDN serves images, CSS and JavaScript from locations near your visitors and takes most of that load off your server.
  • Application caching: a full-page cache for your CMS and an object cache (Redis or Memcached) for database results.
  • Rate limiting with Nginx's limit_req protects login pages and APIs from floods.
  • Scaling out: add application servers behind the load balancer when one server is no longer enough.

8. Monitoring

Enable Nginx's stub_status or Apache's mod_status on a location reachable only from localhost or your own IP, and feed the numbers to a monitoring tool such as Prometheus with Grafana, Zabbix or Netdata. Watch active connections, request rate, response time, 5xx errors and memory. Log the upstream response time in Nginx ($upstream_response_time) to see whether slowness comes from the web server or the application.

9. Where Domain India fits

On Domain India shared hosting, the servers run Apache; on cPanel, Nginx sits in front of Apache as a cache. That is managed for you. Speed up your site with a caching plugin (for WordPress, WP Super Cache or W3 Total Cache), image optimisation and a CDN.

When you need to tune the web server yourself, a VPS gives you full root access. VPS plans are self-managed: you install and configure Apache, Nginx and PHP-FPM, with or without a control panel. Prices on the cards are live and exclude 18% GST.

VPS Basic
₹1,105.30/mo + GST
  • 2 vCPU
  • 4 GB DDR4 RAM
  • 128 GB NVMe SSD Storage
  • 3 TB Monthly Bandwidth
See plan details
VPS Standard
₹2,210.60/mo + GST
  • 4 vCPU
  • 8 GB DDR4 RAM
  • 256 GB NVMe SSD Storage
  • 5 TB Monthly Bandwidth
See plan details
Which Apache MPM is best for high traffic?

The event MPM, with PHP running through PHP-FPM. It handles many connections with little memory and supports HTTP/2. Prefork is needed only for the old mod_php module, which does not work with threaded MPMs.

How do I choose MaxRequestWorkers?

Start from your server's memory and measure. With the event MPM the Apache threads are light, and the memory cost is mostly in PHP-FPM, so size pm.max_children from the RAM available to PHP and set MaxRequestWorkers no higher than ServerLimit times ThreadsPerChild.

Should I put Nginx in front of Apache?

It often helps on busy sites. Nginx handles TLS, static files and page caching efficiently, while Apache keeps .htaccess compatibility for the application. Remember to pass the visitor's IP to Apache with mod_remoteip.

How many worker_connections should Nginx have?

1024 is the usual default; 4096 or more suits busy servers, provided worker_rlimit_nofile and the system's open-file limit are raised to match. A proxied request uses two connections.

Can I change Apache or Nginx settings on Domain India shared hosting?

No. On shared hosting the web server is shared by every account, so its configuration is managed for you. Use a caching plugin, optimise images and use a CDN, or move to a VPS if you need to tune the server.

Does enabling HTTP/2 need any changes?

On Apache, enable mod_http2 with the event MPM and add Protocols h2 http/1.1. On Nginx 1.25.1 and later, add http2 on; to the HTTPS server block; older versions add http2 to the listen line. HTTP/2 works only over HTTPS in browsers.

Ready to take control of your web server? Compare VPS plans, compare Nginx and Caddy as app gateways, or open a support ticket if your site on shared hosting is slow.

Need to tune your own web server?

Self-managed KVM VPS plans with full root access, so you control Apache, Nginx and PHP-FPM.

See VPS plans

Was this article helpful?

Your answer helps us decide what to improve next.

Still need help? Open a support ticket and our team will reply.

Prefer an app? Add this site to your home screen.Get the app
Tune Apache and Nginx for High Traffic | Domain India