Nginx is the backbone of modern web infrastructure: it serves static files, terminates TLS, reverse-proxies, load balances and caches — often all in one process. This guide walks through the configuration patterns sysadmins reach for most — reverse proxy, FastCGI and proxy cache, rate limiting, gzip and HTTP/2 — with examples drawn from real production servers.
Base Directory Layout
Related guides: What is DNS, settings · Domain names & WHOIS lookup · Hosting types guide · Plesk panel guide · Web hosting price comparison
Nginx config lives under /etc/nginx/. The main entry point is nginx.conf; per-site configs go in sites-available/ with a symlink into sites-enabled/. After any edit, nginx -t && systemctl reload nginx is mandatory.
# /etc/nginx/nginx.conf — baseline
worker_processes auto;
worker_rlimit_nofile 65535;
events { worker_connections 4096; multi_accept on; }
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 30s;
keepalive_requests 100;
types_hash_max_size 2048;
server_tokens off;
gzip on;
gzip_vary on;
gzip_comp_level 5;
gzip_min_length 1024;
gzip_types text/plain text/css application/javascript application/json image/svg+xml;
}
Reverse Proxy Setup
Putting Nginx in front of Node.js, Python or Java backends gives you SSL termination, caching, gzip and log control for free. The backend typically listens on a localhost port such as 127.0.0.1:3000, and Nginx forwards public traffic to it.
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
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_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
FastCGI Cache (for PHP)
Hitting PHP-FPM on every request is wasteful for apps like WordPress. FastCGI cache lets Nginx store the rendered page and serve subsequent requests without ever calling PHP. Load drops roughly 10x.
# inside the http { } block
fastcgi_cache_path /var/cache/nginx/fastcgi levels=1:2
keys_zone=WORDPRESS:100m inactive=60m max_size=1g;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
# inside the server { } block
set $skip_cache 0;
if ($request_method = POST) { set $skip_cache 1; }
if ($query_string != "") { set $skip_cache 1; }
if ($request_uri ~* "/wp-admin/|/xmlrpc.php|wp-.*.php|/feed/") { set $skip_cache 1; }
if ($http_cookie ~* "comment_author|wordpress_logged_in|wp-postpass") { set $skip_cache 1; }
location ~ \.php$ {
fastcgi_cache WORDPRESS;
fastcgi_cache_valid 200 60m;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
include fastcgi_params;
add_header X-Cache-Status $upstream_cache_status;
}
Proxy Cache (for Upstream APIs)
A 5- to 60-second cache layer in front of your backend can massively accelerate slow API endpoints. It is ideal for responses that change infrequently and behave like static content.
proxy_cache_path /var/cache/nginx/proxy levels=1:2 keys_zone=APICACHE:50m max_size=500m inactive=30m;
location /api/ {
proxy_cache APICACHE;
proxy_cache_valid 200 30s;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
proxy_cache_lock on;
proxy_pass http://127.0.0.1:3000;
add_header X-Proxy-Cache $upstream_cache_status;
}
Rate Limiting
The first line of defense against brute force and DDoS. limit_req_zone caps request rate per IP, while limit_conn_zone caps concurrent connections.
# inside the http { } block
limit_req_zone $binary_remote_addr zone=global:10m rate=15r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=2r/s;
limit_conn_zone $binary_remote_addr zone=addr:10m;
# inside the server { } block
limit_conn addr 50;
limit_req zone=global burst=30 nodelay;
location /login {
limit_req zone=login burst=3 nodelay;
proxy_pass http://127.0.0.1:3000;
}
burst is exceeded, Nginx returns 503 by default. Add limit_req_status 429; for a more appropriate status code.HTTP/2 and HTTP/3
HTTP/2 is enabled with listen 443 ssl http2;. HTTP/3 (QUIC) needs Nginx 1.25+ and listen 443 quic reuseport;. HTTP/3 shines on mobile connections where packet loss is common.
Security Headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
Common Pitfalls
server_nametypo — the site does not resolve- SSL file paths wrong — Nginx refuses to start
worker_connectionstoo low — connection refused under high trafficclient_max_body_sizetoo small — 413 on uploadsproxy_read_timeouttoo short — 504 on long requests
Modern Web Hosting and Server Infrastructure
A performant web hosting service rests on three infrastructure decisions: NVMe SSD disks (4-6× IOPS over SATA SSD), LiteSpeed Web Server or Nginx + LSCache (9× request capacity over Apache) and CloudLinux + Imunify360 isolation. The hosting provider's control panel (cPanel, Plesk, DirectAdmin), daily backup policy, data center location and support response time make a big difference too. Turkish locations give low latency to local visitors, while Hetzner Frankfurt or OVH Roubaix suit global traffic. As your site grows, transitioning from shared hosting to VPS to dedicated server scales CPU/RAM/disk to your needs.
Conclusion
Memorising directives is not enough to get real value out of Nginx — caching, rate limiting and reverse proxy must be calibrated to your site's traffic profile. For Nginx tuning, WordPress FastCGI cache setup or a reverse-proxy architecture review, the KEYDAL hosting team is happy to help.
Traffic-specific Nginx configuration, cache strategy and security hardening Contact us