Nginx Speed Optimization: A Practical Guide
This guide walks you through the most effective Nginx configuration changes to reduce latency, improve throughput, and lower server load. You’ll learn how to tune caching, compression, keepalive connections, and OS-level settings—each step is concrete and can be applied to a typical production server.
What You’ll Need
- A server running Nginx (any modern version, 1.18+ recommended)
- Root or sudo access to edit Nginx configuration files
- Basic familiarity with the command line
- Access to your Nginx config files (usually
/etc/nginx/nginx.confand files in/etc/nginx/conf.d/or/etc/nginx/sites-available/)
Step-by-Step
- Back up your current configuration. Before changing anything, copy your existing config:
sudo cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak. Also back up any site configs you’ll edit. - Set the number of worker processes. In
nginx.conf, setworker_processes auto;so Nginx uses one worker per CPU core. This maximizes parallel request handling. - Tune worker connections. Set
worker_connectionsto a higher value, like4096or8192. This controls how many simultaneous connections each worker can handle. Monitor memory usage—each connection uses a small amount of RAM. - Enable keepalive connections. In your
httpblock, add:keepalive_timeout 65; keepalive_requests 100;
This allows clients to reuse the same TCP connection for multiple requests, reducing handshake overhead. For upstream servers (like PHP-FPM), add a keepalive pool in your
upstreamblock:upstream php_backend { server 127.0.0.1:9000; keepalive 32; }And in the location that proxies to it, set
proxy_http_version 1.1;andproxy_set_header Connection "";. - Enable gzip compression. In the
httpblock, add:gzip on; gzip_comp_level 5; gzip_min_length 256; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml;
This reduces the size of text-based responses. Avoid compressing already-compressed formats like JPEG or PNG.
- Set up caching for static files. In your server or location blocks, add:
location ~* \.(jpg|jpeg|png|gif|ico|css|js|pdf)$ { expires 30d; add_header Cache-Control "public, no-transform"; }This tells browsers to cache static assets for 30 days, reducing repeat requests.
- Enable open file cache. In the
httpblock, add:open_file_cache max=1000 inactive=20s; open_file_cache_valid 30s; open_file_cache_min_uses 2; open_file_cache_errors on;
This caches file descriptors and metadata, speeding up static file serving.
- Tune TCP settings. In the
httpblock, add:tcp_nopush on; tcp_nodelay on;
tcp_nopushoptimizes sending response headers, whiletcp_nodelayreduces latency for keepalive connections. - Adjust client body buffer size. If your site handles file uploads, set
client_max_body_size 10m;(or your desired limit) andclient_body_buffer_size 16k;to avoid excessive memory usage. - Disable access logs for static files (optional). For high-traffic static content, you can skip logging to reduce I/O:
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ { access_log off; ... }Be aware this loses audit trail for those files.
- Use HTTP/2. In your server block, add
listen 443 ssl http2;(if using SSL). HTTP/2 multiplexes requests over a single connection, significantly improving page load times. - Set up microcaching for dynamic content (advanced). If using PHP-FPM, you can cache responses for a short time (e.g., 1-5 seconds) to reduce backend load:
fastcgi_cache_path /var/cache/nginx levels=1:2 keys_zone=mycache:100m inactive=60m; fastcgi_cache_key "$scheme$request_method$host$request_uri"; location ~ \.php$ { ... fastcgi_cache mycache; fastcgi_cache_valid 200 1s; }Test carefully—this can serve stale data if not configured properly.
- Test and reload. After all changes, run
sudo nginx -tto check for syntax errors. If OK, reload withsudo systemctl reload nginx(orsudo nginx -s reload).
Common Mistakes
- Setting worker_processes too high (e.g., 8 on a 2-core server) can cause overhead and memory waste. Use
auto. - Enabling gzip for images wastes CPU and may increase file size. Stick to text-based types.
- Forgetting to set
proxy_http_version 1.1when using keepalive to upstreams—it silently disables keepalive. - Overly long cache expiration for HTML files can cause users to see stale content. Cache only assets with hashed filenames or short expiry.
- Not testing after changes—a typo can take your site down. Always run
nginx -tfirst.
Tips
- Use
gzip_static on;to serve pre-compressed files if you have them (e.g., .gz versions). This saves CPU. - Monitor your server with tools like
htopandnginx -Tto see effective config. - Consider using a CDN for static assets—Nginx still handles dynamic requests faster.
- For SSL, enable session resumption:
ssl_session_cache shared:SSL:10m;andssl_session_timeout 10m;to reduce handshake overhead.
Troubleshooting
- If Nginx fails to start after changes: Run
sudo nginx -tto see the exact error. Common issues are missing semicolons or incorrect file paths. - If performance doesn’t improve: Check if your bottleneck is actually the backend (PHP, database). Use
topto see CPU usage. If Nginx is idle but backend is busy, focus on caching and upstream tuning. - If you see “worker_connections are not enough” errors: Increase
worker_connectionsor reduce the number of keepalive connections. - If gzip isn’t working: Verify that the response has
Content-Typematching yourgzip_types. Use curl -H “Accept-Encoding: gzip” -I to check. - If caching doesn’t seem to work: Check that the cache directory exists and is writable by the Nginx user. Also ensure your cache key is unique enough.
Frequently Asked Questions
How much of a speed improvement can I expect?
It varies. For static files, enabling caching and keepalive can reduce latency by 30-50%. For dynamic content, microcaching can cut response times dramatically if your backend is slow.
Should I use gzip or brotli?
Brotli generally compresses better, but requires the Nginx brotli module. If you can’t install it, gzip is fine. You can enable both, with brotli preferred when the client supports it.
Is it safe to disable access logs?
For high-traffic static files, yes, but you lose the ability to audit those requests. Consider keeping logs for dynamic pages and security-critical endpoints.
What is the ideal worker_connections value?
It depends on your RAM and expected traffic. Start with 4096 and monitor. Each connection uses about 0.5-1 KB of memory, so 8192 connections per worker is safe on a 4GB server.
Does HTTP/2 require SSL?
Yes, in practice. HTTP/2 is implemented over TLS in modern browsers, so you need a valid SSL certificate. Use Let’s Encrypt if you don’t have one.
Can I use these settings on a shared hosting plan?
No. These changes require access to Nginx configuration files, which shared hosting typically doesn’t provide. You’ll need a VPS or dedicated server.
Related
Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.