f in x
> cd .. / HUB_EDITORIALE
Sistemi Operativi & Sicurezza

Nginx Performance Tuning — Cache, Keepalive and Worker Process for Faster Servers

[2026-06-27] Author: Ing. Calogero Bono
Zenithby Meteora Web Il sistema operativo della tua attività. Social, clienti, prenotazioni e fatture in un'unica piattaforma. Palestre, barber, professionisti. Scopri Zenith Demo gratis · senza carta

Your Nginx server works, but you feel it could push harder. Pages load in 2 seconds instead of 800ms, CPU usage spikes for no reason, and concurrent connections seem to choke. The problem is almost never the machine: it's the default configuration. We at Meteora Web see it every time we take over a new server — worker processes, keepalive, and cache are the three levers that separate a sluggish server from a flying one. And you don't need to be a ten-year sysadmin to tune them — you need to understand what each directive does and where to apply it. In this guide we go straight to the point: three intervention areas, ready-to-copy code, and the logic to avoid breaking anything.

How much do worker processes and worker connections impact performance?

This is the first question clients ask when we look at their nginx.conf: «Should I set worker_processes to 8 or 4?». The answer is more subtle than it seems. Nginx is not Apache: it doesn't spawn a process per connection. It uses an asynchronous event-driven model. This means that a single worker can handle thousands of simultaneous connections — if configured correctly.

The correct number of workers

The classic rule: one worker per CPU core. On modern servers with hyper-threading, the value auto is the best choice because Nginx detects the number of available cores. Never force a number higher than the physical cores: you only increase context switching without gain. Here's the base configuration:

Sponsored Protocol

worker_processes auto;
worker_rlimit_nofile 65535;

worker_rlimit_nofile raises the file descriptor limit for each worker — essential if you manage many connections. By default the OS has low limits (1024). Without this, even with workers at full capacity, the server will reject connections.

worker_connections: how much can a single worker handle?

The worker_connections directive defines the maximum number of concurrent connections a single worker can open. The total theoretical max is worker_processes × worker_connections. For a medium server (4 cores, 8GB RAM) with normal traffic, 4096 is a solid starting point:

events {
    worker_connections 4096;
    multi_accept on;
    use epoll;
}
  • multi_accept on: makes the worker accept multiple connections at once. On Linux with epoll it's mandatory to avoid dropping packets.
  • use epoll: forces Nginx to use the most efficient multiplexing method on Linux. It auto-detects by default, but being explicit doesn't hurt.

Common mistake: setting worker_connections to 65535 without raising worker_rlimit_nofile. The OS blocks the worker before Nginx even tries to manage that many connections. Always check system limits with ulimit -n and, if needed, modify /etc/security/limits.conf.

Sponsored Protocol

How to configure keepalive to reduce connection overhead?

Each TCP connection costs: handshake, TLS negotiation (if HTTPS), teardown. If the browser opens a connection for every single resource (CSS, JS, images), clients pile up and the server gets congested. Keepalive reuses the same connection for multiple requests, reducing latency and load. The difference? A server without keepalive might handle 100 users with 1000 requests; with well-tuned keepalive, the same 100 users produce 100 requests — the rest are reused.

Proxy to backend (PHP, Node, etc.)

Most traffic flows from Nginx to a backend (PHP-FPM, Gunicorn, Node). Here keepalive is doubly crucial: it avoids opening a new TCP connection for every request to the backend. Basic upstream configuration:

upstream php_backend {
    server 127.0.0.1:9000;
    keepalive 32;
}

server {
    location ~ \.php$ {
        fastcgi_pass php_backend;
        fastcgi_keep_conn on;
        include fastcgi_params;
    }
}

keepalive 32 tells Nginx to keep up to 32 connections open to the backend for reuse. fastcgi_keep_conn on enables keepalive for the FastCGI backend. Without this, connections are closed after each request — making the upstream keepalive directives useless.

Sponsored Protocol

Keepalive on the client side (visitor)

For end clients, Nginx by default keeps the connection open for 75 seconds (keepalive_timeout 75). This works for static sites, but on dynamic applications with login and AJAX you can reduce it to 30 seconds to free resources faster. Here's a typical balanced config:

keepalive_timeout 30;
keepalive_requests 100;

keepalive_requests 100 limits the number of requests on a single keepalive connection. After 100 requests, the connection is closed and a new one is created. Useful to prevent a connection from staying open too long on a faulty client.

Which caching directives to use to reduce load and speed up responses?

Caching in Nginx is a performance silo: when configured properly, static pages or API responses are served from cache without touching the backend. We at Meteora Web have reduced CPU load by 70% on a seasonal ecommerce site just by enabling proxy_cache for non-personalized product pages. The trick is knowing what to cache and for how long.

Proxy cache for reverse proxy (dynamic pages)

If Nginx acts as a proxy for an application (WordPress, Laravel, API), you can store responses in a disk cache. Basic configuration:

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=mycache:10m max_size=1g inactive=60m use_temp_path=off;

server {
    location / {
        proxy_cache mycache;
        proxy_cache_key "$scheme$request_method$host$request_uri";
        proxy_cache_valid 200 302 60m;
        proxy_cache_valid 404 1m;
        proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
        proxy_pass http://backend;
    }
}
  • proxy_cache_path: defines where to store the cache. levels=1:2 organizes files in subdirectories to avoid too many files in one folder. max_size=1g limits the cache to 1GB. inactive=60m removes files not accessed for 60 minutes.
  • proxy_cache_valid: how long to cache responses with status 200/302 (60 minutes) and 404 (1 minute).
  • proxy_cache_use_stale: if the backend crashes, Nginx serves the stale cached version — better than a 502 error.

FastCGI cache for PHP

For WordPress, Laravel, and other PHP CMSs, fastcgi_cache is even more direct: caching the rendered pages from PHP-FPM. Typical configuration:

Sponsored Protocol

fastcgi_cache_path /var/cache/nginx/fastcgi levels=1:2 keys_zone=phpcache:10m max_size=2g inactive=60m use_temp_path=off;

server {
    location ~ \.php$ {
        fastcgi_cache phpcache;
        fastcgi_cache_key "$scheme$request_method$host$request_uri";
        fastcgi_cache_valid 200 60m;
        fastcgi_pass php_backend;
    }
}

Warning: do not cache pages with session cookies or personalized content. You can exclude them with fastcgi_cache_bypass $http_cookie if the cookie contains an authentication token. Otherwise you risk serving one user's cached page to another.

Sponsored Protocol

What to do next

You have three levers to pull right now:

  1. Check your current configuration: on your server run nginx -t to verify syntax, then nginx -T | grep -E 'worker_processes|worker_connections|keepalive|proxy_cache' to see what you have now.
  2. Apply changes gradually: one directive at a time. Reload Nginx with nginx -s reload and test with ab -n 1000 -c 100 https://yoursite.com/ (Apache Benchmark) or wrk to measure requests per second and latency.
  3. Monitor with htop or netdata: watch CPU, memory, active connections (nginx -s reopen logs?). If you see spikes of exhausted worker_connections (503 errors in logs), increase the value and check ulimit.
  4. Don't forget security: a cache must be periodically cleaned and protected from unauthorized access. Use proxy_cache_purge if you need to invalidate cache on demand.

For a complete foundation on Nginx in production, read our pillar guide on Nginx and Web Server Configuration. There we cover virtual hosts, SSL, logging, and much more — everything you need to never have excuses again.

Ing. Calogero Bono

> AUTHOR_EXTRACTED

Ing. Calogero Bono

Ingegnere Informatico, co-fondatore di Meteora Web. Esperto in architetture software, sicurezza informatica e sviluppo sistemi scalabili.
[ Read Full Dossier ]

> METEORA_WEB // DIGITAL AGENCY

We build the digital presence your business deserves.

Websites, social media, online advertising, e-commerce and high-performance hosting, engineered with method by computer engineers in Sciacca, for all of Italy.

> MW_JOURNAL

> READ_ALL()