← Course Index

Nginx Performance & Security

~25 min · Nginx

Ref
Primary Source
NGINX Official Docs — ngx_http_gzip_module & Security

Authoritative reference for gzip, rate limiting, and security header directives. Read →

Gzip Compression

Enable gzip to compress responses — typically 60-70% size reduction for HTML/CSS/JS:

# Add to nginx.conf http{} block or your server block
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_comp_level 6;
gzip_proxied any;
gzip_types
  text/plain
  text/css
  text/javascript
  application/javascript
  application/json
  application/xml
  image/svg+xml;
💡

Never gzip images (PNG, JPG, WebP) — they're already compressed. Gzip only compresses text-based content effectively.

Caching Headers

# Cache static assets aggressively (they have hashed filenames)
location ~* \.(js|css|png|jpg|svg|ico|woff2)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
}

# Never cache HTML (it changes on every deploy)
location / {
    add_header Cache-Control "no-cache, no-store, must-revalidate";
    proxy_pass http://localhost:3000;
}

Rate Limiting

# Define rate limit zone (in http{} block)
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;

# Apply to location (allow burst of 20, then queue)
location /api/ {
    limit_req zone=api burst=20 nodelay;
    proxy_pass http://localhost:3000;
}

# Block IPs that exceed limits with 429
limit_req_status 429;
⚠️

Rate limiting in Nginx doesn't replace application-level rate limiting in Redis. Use both: Nginx stops DDoS, Redis handles per-user API quotas.

Security Headers

# Add to your server block (these are the essentials)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;

# Content Security Policy — tune per app
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline';" always;

# Hide Nginx version from attackers
server_tokens off;

Client Body & Timeout Settings

# Prevent large request body attacks
client_max_body_size 10m;   # Limit upload size (default 1m)
client_body_timeout 10s;
client_header_timeout 10s;

# Timeout for proxied requests
proxy_read_timeout 60s;
proxy_connect_timeout 10s;

# Keep connections alive (performance)
keepalive_timeout 65;
keepalive_requests 100;

Check Your Understanding

1. Which content type should NOT be gzip compressed?
2. You want static JS files (with hashed filenames like app.a1b2c3.js) to be cached for a year. Which Cache-Control header?
3. Which Nginx directive hides version information from potential attackers in response headers?