Skip to Content
chalvien 1.0 is released

NGINX Configuration

Here is a production-ready NGINX configuration:

  • Express API (port 4000)
  • Next.js app (port 3000)
  • Docker Compose stack
  • Hostinger VPS
  • SSL via Let’s Encrypt
  • Security hardened

This assumes:

  • Domain: yourdomain.com
  • Certbot already issued certificates
  • Containers: web, api

Production NGINX Config (SSL + Security)

Replace your /nginx/default.conf with:

default.conf
# ------------------------- # HTTP → HTTPS Redirect # ------------------------- server { listen 80; server_name yourdomain.com www.yourdomain.com; location /.well-known/acme-challenge/ { root /var/www/certbot; } location / { return 301 https://$host$request_uri; } } # ------------------------- # HTTPS Server # ------------------------- server { listen 443 ssl http2; server_name yourdomain.com www.yourdomain.com; # SSL certificates ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem; # Modern TLS settings ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; ssl_session_timeout 1d; ssl_session_cache shared:SSL:10m; # OCSP stapling ssl_stapling on; ssl_stapling_verify on; # ------------------------- # Security Headers # ------------------------- add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; # HSTS (enable only after confirming HTTPS works) add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always; # Basic Content Security Policy (adjust if needed) add_header Content-Security-Policy " default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; font-src 'self' data:; frame-ancestors 'self'; " always; # Hide nginx version server_tokens off; # ------------------------- # API Proxy # ------------------------- location /api/ { proxy_pass http://api:4000/; 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_read_timeout 60s; proxy_connect_timeout 60s; } # ------------------------- # Next.js Proxy # ------------------------- location / { proxy_pass http://web: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_read_timeout 60s; } # ------------------------- # Gzip Compression # ------------------------- gzip on; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; gzip_min_length 1024; }

Important: Mount Certificates in Docker

If using Docker Compose, update nginx service:

nginx: image: nginx:alpine restart: always ports: - "80:80" - "443:443" volumes: - ./nginx/default.conf:/etc/nginx/conf.d/default.conf - /etc/letsencrypt:/etc/letsencrypt:ro - ./certbot/www:/var/www/certbot depends_on: - web - api

Run on VPS (outside Docker initially):

sudo certbot certonly --standalone -d yourdomain.com -d www.yourdomain.com

After certificate generation:

  • Restart docker
  • Enable HSTS only after verifying HTTPS works

Production Notes (Very Important)

1. Adjust CSP for Clerk

If you use Clerk or other third-party auth:

Add:

script-src 'self' https://clerk.your-region.com; connect-src 'self' https://api.clerk.com;

CSP must match your real external domains.

2. Rate Limiting (Optional but Recommended)

Add inside HTTPS server block:

limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s; location /api/ { limit_req zone=api_limit burst=20 nodelay; ... }

3. Large Uploads (If Needed)

If handling images:

client_max_body_size 20M;

Security Checklist

You now have:

  • HTTPS only
  • HSTS
  • CSP
  • Secure headers
  • Gzip
  • Proxy headers
  • HTTP → HTTPS redirect
  • TLS 1.2/1.3 only
  • Hidden nginx version

Final Result

Production-grade reverse proxy ready for:

  • Next.js
  • Express API
  • Postgres
  • Kotlin mobile app
  • Clerk (with CSP adjustment)