NGINX

Referensi konfigurasi NGINX yang komprehensif: server block, location, reverse proxy, SSL/TLS, load balancing, rate limiting, security headers, logging, dan performance tuning.

00

Instalasi & Manajemen Service

Install, cek versi, dan kelola NGINX dengan systemctl.

Install

deb
# Debian / Ubuntu
apt update && apt install -y nginx

# Tambah repo nginx.org untuk versi stable terbaru
curl -fsSL https://nginx.org/keys/nginx_signing.key | gpg --dearmor   -o /etc/apt/keyrings/nginx.gpg
echo "deb [signed-by=/etc/apt/keyrings/nginx.gpg]   http://nginx.org/packages/ubuntu $(lsb_release -cs) nginx"   > /etc/apt/sources.list.d/nginx.list
apt update && apt install -y nginx
rhel
# RHEL / CentOS / Rocky / Alma
dnf install -y nginx

# Atau dari repo nginx.org
cat > /etc/yum.repos.d/nginx.repo << 'EOF'
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
EOF
dnf install -y nginx

Versi dan build info

shell
nginx -v                   # versi singkat
nginx -V                   # versi + compile flags + module yang terinstall

Manajemen service

shell
systemctl enable --now nginx    # aktifkan dan jalankan sekarang
systemctl status nginx
systemctl start nginx
systemctl stop nginx
systemctl restart nginx         # stop + start (downtime singkat)
systemctl reload nginx          # reload config tanpa downtime (graceful)

Test config dan signal

shell
nginx -t                    # test konfigurasi — wajib sebelum reload
nginx -T                    # test + dump seluruh config yang sudah di-parse
nginx -s reload             # kirim signal HUP (graceful reload)
nginx -s reopen             # buka ulang log file (setelah logrotate)
nginx -s quit               # graceful shutdown (tunggu request selesai)
nginx -s stop               # immediate shutdown

Struktur direktori default

shell
/etc/nginx/nginx.conf            # file konfigurasi utama
/etc/nginx/conf.d/               # config tambahan (di-include oleh nginx.conf)
/etc/nginx/sites-available/      # server block tersedia (Debian/Ubuntu)
/etc/nginx/sites-enabled/        # symlink ke sites-available yang aktif
/var/log/nginx/access.log        # access log
/var/log/nginx/error.log         # error log
/var/www/html/                   # document root default
/usr/share/nginx/html/           # document root (RHEL/Alpine)
/run/nginx.pid                   # PID file

Enable site (Debian/Ubuntu pattern)

shell
# Buat config di sites-available, lalu buat symlink
ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/myapp
nginx -t && systemctl reload nginx

# Disable
rm /etc/nginx/sites-enabled/myapp
nginx -t && systemctl reload nginx
01

Struktur Konfigurasi

Hirarki konteks dan anatomi nginx.conf.

Hirarki konteks

nginx
# main context — global settings
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /run/nginx.pid;

events {                         # event processing
    worker_connections 1024;
}

http {                           # HTTP settings (shared oleh semua server)
    include mime.types;
    sendfile on;
    keepalive_timeout 65;

    server {                     # virtual host
        listen 80;
        server_name example.com;

        location / {             # request routing
            root /var/www/html;
        }
    }
}

nginx.conf minimal — production

nginx
user nginx;
worker_processes auto;
worker_rlimit_nofile 65535;
error_log /var/log/nginx/error.log warn;
pid /run/nginx.pid;

events {
    worker_connections 4096;
    use epoll;
    multi_accept on;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    charset utf-8;
    server_tokens off;              # sembunyikan versi NGINX di response header

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    keepalive_requests 1000;

    # Gzip
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_types text/plain text/css application/json application/javascript
               text/xml application/xml image/svg+xml;

    # Logging
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent"';
    access_log /var/log/nginx/access.log main;

    include /etc/nginx/conf.d/*.conf;
}

Include pattern

nginx
include /etc/nginx/mime.types;
include /etc/nginx/conf.d/*.conf;         # semua file .conf di direktori
include /etc/nginx/sites-enabled/*;       # pattern tanpa ekstensi
02

Server Block (Virtual Host)

Definisi virtual host untuk melayani domain dan port berbeda.

HTTP server block dasar

nginx
server {
    listen 80;
    listen [::]:80;                 # IPv6
    server_name example.com www.example.com;

    root /var/www/example.com;
    index index.html index.htm;

    location / {
        try_files $uri $uri/ =404;
    }
}

HTTPS server block

nginx
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name example.com www.example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    root /var/www/example.com;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}

Redirect HTTP → HTTPS

nginx
server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;
    return 301 https://example.com$request_uri;
}

Redirect www → non-www (atau sebaliknya)

nginx
# www → non-www
server {
    listen 443 ssl http2;
    server_name www.example.com;
    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    return 301 https://example.com$request_uri;
}

# non-www → www
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;
    return 301 https://www.example.com$request_uri;
}

Default server — catch-all

nginx
server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;                  # _ = match semua nama yang tidak cocok
    return 444;                     # tutup koneksi tanpa response
}

# Atau tampilkan halaman 403:
server {
    listen 80 default_server;
    server_name _;
    return 403;
}

Multiple domain dan wildcard

nginx
server {
    server_name example.com www.example.com;     # multiple nama
    server_name *.example.com;                   # wildcard subdomain
    server_name example.com ~^wwwd+.;          # campuran nama dan regex
}
03

Location Block

Routing request berdasarkan URI. Urutan prioritas penting — baca catatan.

Modifier dan prioritas

nginx
location = /favicon.ico { }    # exact match — prioritas tertinggi
location ^~ /images/ { }       # prefix, stop cek regex jika cocok
location ~  \.php$ { }        # regex case-sensitive
location ~* \.png$ { }        # regex case-insensitive
location /api/ { }             # prefix match biasa
location / { }                 # fallback — cocok dengan semua URI

root vs alias

nginx
# root — URI /images/cat.png → /var/www/html/images/cat.png
location /images/ {
    root /var/www/html;
}

# alias — URI /images/cat.png → /data/pics/cat.png
location /images/ {
    alias /data/pics/;    # trailing slash wajib pada alias
}

try_files

nginx
# Coba file, lalu direktori, lalu 404
location / {
    try_files $uri $uri/ =404;
}

# SPA (React/Vue/Angular) — semua route ke index.html
location / {
    try_files $uri $uri/ /index.html;
}

# PHP — coba file statis, lalu pass ke PHP-FPM
location / {
    try_files $uri $uri/ @php;
}
location @php {
    fastcgi_pass unix:/run/php/php8.2-fpm.sock;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}

Named location

nginx
# Named location dimulai dengan @ — hanya bisa di-referensi internal
location / {
    try_files $uri $uri/ @backend;
}

location @backend {
    proxy_pass http://127.0.0.1:3000;
    include /etc/nginx/proxy_params;
}

Nested location

nginx
location /api/ {
    proxy_pass http://127.0.0.1:3000;

    location /api/public/ {
        proxy_pass http://127.0.0.1:3000;
        limit_req zone=public_api burst=50;
    }

    location /api/admin/ {
        allow 10.0.0.0/8;
        deny all;
        proxy_pass http://127.0.0.1:3000;
    }
}
04

Reverse Proxy

Konfigurasi NGINX sebagai reverse proxy ke aplikasi backend.

proxy_pass dasar

nginx
server {
    listen 80;
    server_name app.example.com;

    location / {
        proxy_pass http://127.0.0.1:3000;    # backend URL
        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 Connection        "";   # hapus untuk HTTP/1.1 keepalive
    }
}

proxy_params — file reusable

nginx
# /etc/nginx/proxy_params
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 X-Forwarded-Host  $host;
proxy_set_header Connection        "";

# Gunakan di location:
# include /etc/nginx/proxy_params;

Proxy timeout dan buffering

nginx
location / {
    proxy_pass http://127.0.0.1:3000;
    include /etc/nginx/proxy_params;

    # Timeout
    proxy_connect_timeout  10s;    # batas waktu koneksi ke backend
    proxy_send_timeout     60s;    # batas waktu kirim request ke backend
    proxy_read_timeout     60s;    # batas waktu tunggu response backend

    # Buffering — aktifkan untuk throughput lebih baik
    proxy_buffering         on;
    proxy_buffer_size       4k;
    proxy_buffers           8 16k;
    proxy_busy_buffers_size 32k;

    # Matikan buffering untuk SSE / streaming
    # proxy_buffering off;
}

WebSocket proxy

nginx
location /ws/ {
    proxy_pass http://127.0.0.1:3000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade    $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host       $host;
    proxy_read_timeout 3600s;    # 1 jam — WS perlu koneksi panjang
    proxy_send_timeout 3600s;
}

Proxy untuk backend HTTPS

nginx
location / {
    proxy_pass https://backend.internal:8443;
    proxy_ssl_verify        on;
    proxy_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;
    proxy_ssl_session_reuse on;
}

Error dari backend — intercept

nginx
location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_intercept_errors on;   # tangkap error dari backend
    error_page 502 503 504 /50x.html;
}

location = /50x.html {
    root /usr/share/nginx/html;
    internal;                    # hanya bisa diakses secara internal
}

Sub-path proxy — strip prefix

nginx
# Request /api/users → backend menerima /users
location /api/ {
    proxy_pass http://127.0.0.1:3000/;   # trailing slash = strip /api/
}

# Request /api/users → backend menerima /api/users (tidak strip)
location /api/ {
    proxy_pass http://127.0.0.1:3000;    # tanpa trailing slash
}
05

SSL / TLS

Konfigurasi HTTPS yang aman: protokol, cipher, sertifikat, dan Let's Encrypt.

SSL modern — TLSv1.2 + TLSv1.3

nginx
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:
            ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:
            ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers off;    # TLSv1.3 memilih cipher sendiri

Session cache dan DH params

nginx
ssl_session_cache   shared:SSL:10m;   # cache 10MB antar worker
ssl_session_timeout 1d;
ssl_session_tickets off;           # nonaktifkan untuk forward secrecy

# Generate DH params (jalankan sekali):
# openssl dhparam -out /etc/nginx/dhparam.pem 2048
ssl_dhparam /etc/nginx/dhparam.pem;

OCSP Stapling

nginx
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
resolver 1.1.1.1 8.8.8.8 valid=300s;
resolver_timeout 5s;

HSTS — HTTP Strict Transport Security

nginx
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

Konfigurasi SSL lengkap per server

nginx
server {
    listen 443 ssl http2;
    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;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;
    ssl_session_cache   shared:SSL:10m;
    ssl_session_timeout 1d;
    ssl_session_tickets off;
    ssl_dhparam         /etc/nginx/dhparam.pem;

    ssl_stapling        on;
    ssl_stapling_verify on;
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
    resolver 1.1.1.1 8.8.8.8 valid=300s;

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}

Let's Encrypt — Certbot

shell
# Install certbot
apt install -y certbot python3-certbot-nginx

# Issue sertifikat + auto-configure NGINX
certbot --nginx -d example.com -d www.example.com

# Issue sertifikat saja (tanpa ubah config)
certbot certonly --nginx -d example.com

# Wildcard (butuh DNS challenge)
certbot certonly --manual --preferred-challenges dns   -d "*.example.com" -d example.com

# Renew manual
certbot renew --dry-run    # test renew
certbot renew              # renew semua sertifikat yang hampir expired

# Cek tanggal expiry
certbot certificates

Self-signed certificate (dev/internal)

shell
openssl req -x509 -nodes -days 365 -newkey rsa:2048   -keyout /etc/nginx/ssl/self.key   -out    /etc/nginx/ssl/self.crt   -subj "/CN=localhost"

# Atau dengan SAN (Subject Alternative Names)
openssl req -x509 -nodes -days 365 -newkey rsa:2048   -keyout /etc/nginx/ssl/dev.key   -out    /etc/nginx/ssl/dev.crt   -subj "/CN=dev.local"   -addext "subjectAltName=DNS:dev.local,DNS:*.dev.local,IP:127.0.0.1"

Mutual TLS (mTLS) — autentikasi client

nginx
server {
    ssl_client_certificate /etc/nginx/ssl/ca.crt;   # CA yang menandatangani client cert
    ssl_verify_client on;                            # wajib, tolak jika tidak ada cert
    # ssl_verify_client optional;                   # opsional, cek $ssl_client_verify

    location /api/ {
        # Hanya izinkan jika client cert valid
        if ($ssl_client_verify != SUCCESS) { return 403; }
        proxy_pass http://127.0.0.1:3000;
    }
}
06

File Statis & Cache

Serve file statis secara efisien dengan cache header yang tepat.

Serve static files

nginx
server {
    listen 80;
    server_name static.example.com;
    root /var/www/static;

    location / {
        try_files $uri $uri/ =404;
        autoindex off;              # matikan directory listing
    }

    # Asset dengan hash di nama file — cache agresif
    location ~* \.(?:js|css|woff2|woff|ttf)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
        access_log off;
    }

    # Gambar
    location ~* \.(?:jpg|jpeg|png|gif|ico|svg|webp|avif)$ {
        expires 30d;
        add_header Cache-Control "public, must-revalidate";
        access_log off;
    }

    # HTML — jangan di-cache
    location ~* \.html$ {
        expires -1;
        add_header Cache-Control "no-store";
    }
}

Gzip compression

nginx
# Di konteks http {}
gzip on;
gzip_vary on;                       # tambahkan Vary: Accept-Encoding
gzip_proxied any;                   # compress untuk semua proxied request
gzip_comp_level 6;                  # 1 (cepat) – 9 (kecil); 6 = sweet spot
gzip_min_length 1024;               # tidak compress file < 1KB
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types
    text/plain
    text/css
    text/javascript
    application/javascript
    application/json
    application/xml
    application/rss+xml
    image/svg+xml
    font/woff2;
# image/png, image/jpeg tidak perlu (sudah terkompresi)

Open file cache

nginx
open_file_cache max=10000 inactive=30s;
open_file_cache_valid    60s;
open_file_cache_min_uses 2;
open_file_cache_errors   on;

Proxy cache — cache response backend

nginx
# Di konteks http {}
proxy_cache_path /var/cache/nginx
    levels=1:2
    keys_zone=app_cache:10m      # nama zone dan ukuran key store (10MB)
    max_size=1g                  # batas total cache di disk
    inactive=60m                 # hapus jika tidak diakses 60 menit
    use_temp_path=off;

# Di location {}
location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_cache             app_cache;
    proxy_cache_valid       200 302  10m;   # cache 200/302 selama 10 menit
    proxy_cache_valid       404      1m;
    proxy_cache_use_stale   error timeout updating;
    proxy_cache_lock        on;
    add_header X-Cache-Status $upstream_cache_status;  # debug: HIT/MISS/BYPASS
}
07

Load Balancing

Distribusi traffic ke multiple backend server.

Upstream block dan algoritma

nginx
upstream app_backend {
    # round-robin (default, tidak perlu direktif)
    server 10.0.0.1:3000;
    server 10.0.0.2:3000;
    server 10.0.0.3:3000;
}

upstream app_least_conn {
    least_conn;              # pilih server dengan koneksi aktif paling sedikit
    server 10.0.0.1:3000;
    server 10.0.0.2:3000;
}

upstream app_sticky {
    ip_hash;                 # sticky session berdasarkan IP client
    server 10.0.0.1:3000;
    server 10.0.0.2:3000;
}

upstream app_hash {
    hash $request_uri consistent;  # sticky berdasarkan URI
    server 10.0.0.1:3000;
    server 10.0.0.2:3000;
}

Weight, backup, dan down

nginx
upstream app_backend {
    server 10.0.0.1:3000 weight=3;      # terima 3x lebih banyak traffic
    server 10.0.0.2:3000 weight=1;
    server 10.0.0.3:3000 backup;        # hanya aktif jika server lain down
    server 10.0.0.4:3000 down;          # tandai sebagai offline sementara
}

Health check pasif

nginx
upstream app_backend {
    server 10.0.0.1:3000 max_fails=3 fail_timeout=30s;
    # max_fails=3  : tandai down setelah 3 kali gagal dalam fail_timeout
    # fail_timeout : durasi menandai down DAN jendela waktu hitung kegagalan
    server 10.0.0.2:3000 max_fails=3 fail_timeout=30s;
}

Keepalive connection ke upstream

nginx
upstream app_backend {
    server 10.0.0.1:3000;
    server 10.0.0.2:3000;
    keepalive 32;                        # pool 32 koneksi idle per worker
}

# Di location, tambahkan:
location / {
    proxy_pass http://app_backend;
    proxy_http_version 1.1;
    proxy_set_header Connection "";      # wajib untuk keepalive upstream
}

Proxy ke Unix socket

nginx
upstream php_fpm {
    server unix:/run/php/php8.2-fpm.sock;
}

upstream node_app {
    server unix:/run/node/app.sock;
}
08

Security Headers

Header HTTP yang meningkatkan keamanan browser.

Security headers esensial

nginx
# Sembunyikan versi NGINX
server_tokens off;

# Cegah clickjacking
add_header X-Frame-Options "SAMEORIGIN" always;

# Cegah MIME-type sniffing
add_header X-Content-Type-Options "nosniff" always;

# XSS protection (browser lama)
add_header X-XSS-Protection "1; mode=block" always;

# Referrer policy
add_header Referrer-Policy "strict-origin-when-cross-origin" always;

# HSTS
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

# Permissions Policy (batasi API browser)
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;

Content Security Policy (CSP)

nginx
# Mode report-only dulu (tidak blokir, hanya lapor)
add_header Content-Security-Policy-Report-Only
  "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; report-uri /csp-report" always;

# Mode enforcement
add_header Content-Security-Policy
  "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'" always;

Sembunyikan informasi server

nginx
server_tokens off;          # hapus versi NGINX dari header Server dan error page

# Header Server masih menampilkan "nginx" — untuk menyembunyikan sepenuhnya
# butuh modul ngx_http_headers_more (tersedia di nginx-extras di Debian):
# more_clear_headers Server;
# more_set_headers   "Server: webserver";
09

Rate Limiting & Access Control

Batasi request rate, koneksi bersamaan, dan akses berdasarkan IP.

limit_req — rate limiting request

nginx
# Di konteks http {}
limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=login_limit:10m rate=5r/m;
limit_req_status 429;           # kode HTTP saat di-limit (default 503)

# Di location {}
location /api/ {
    limit_req zone=req_limit burst=20 nodelay;
}

location /auth/login {
    limit_req zone=login_limit burst=3 nodelay;
}

limit_conn — batasi koneksi bersamaan

nginx
# Di konteks http {}
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
limit_conn_status 429;

# Di location {}
location /download/ {
    limit_conn conn_limit 5;    # maks 5 koneksi bersamaan per IP
    limit_rate 500k;            # throttle bandwidth per koneksi (500 KB/s)
    limit_rate_after 5m;        # mulai throttle setelah 5MB pertama
}

Allow / Deny — IP whitelist/blacklist

nginx
# Whitelist — hanya izinkan IP/range tertentu
location /admin/ {
    allow 10.0.0.0/8;           # jaringan internal
    allow 192.168.1.0/24;       # LAN
    allow 203.0.113.5;          # IP spesifik
    deny all;                   # blokir semua yang lain
}

# Blacklist — blokir IP tertentu
location / {
    deny 198.51.100.0/24;
    deny 203.0.113.99;
    allow all;
}

geo — klasifikasi berdasarkan IP

nginx
# Di konteks http {}
geo $is_internal {
    default 0;
    10.0.0.0/8     1;
    172.16.0.0/12  1;
    192.168.0.0/16 1;
    127.0.0.1      1;
}

geo $country_block {
    default 0;
    # Isi dengan range IP sesuai geolocation database
}

# Di location {}
location / {
    if ($is_internal = 0) {
        limit_req zone=req_limit burst=10;
    }
}

map — transform variabel

nginx
# Di konteks http {}
map $http_user_agent $is_bot {
    default        0;
    ~*bot          1;
    ~*crawler      1;
    ~*spider       1;
    ~*scrapy       1;
}

map $request_method $cors_method {
    OPTIONS 1;
    default 0;
}

# Di location {}
location / {
    if ($is_bot) { return 403; }
}

Basic Auth

shell
# Buat file password (install apache2-utils dulu)
htpasswd -c /etc/nginx/.htpasswd user1
htpasswd /etc/nginx/.htpasswd user2    # tambah user berikutnya
nginx
location /private/ {
    auth_basic "Area Terbatas";
    auth_basic_user_file /etc/nginx/.htpasswd;
}

Blokir user agent dan request mencurigakan

nginx
# Di konteks server {}

# Blokir user agent kosong
if ($http_user_agent = "") { return 444; }

# Blokir metode yang tidak diperlukan
if ($request_method !~ ^(GET|HEAD|POST|PUT|DELETE|PATCH|OPTIONS)$) {
    return 405;
}

# Blokir akses ke file tersembunyi (.git, .env, dll.)
location ~ /\. {
    deny all;
    access_log off;
    log_not_found off;
}
10

Redirect & Rewrite

Atur ulang URL dengan return dan rewrite.

return — redirect sederhana

nginx
# Kode redirect
return 301 https://example.com$request_uri;   # permanent
return 302 https://example.com/sale/;         # temporary
return 307 https://example.com$request_uri;   # temporary, pertahankan method
return 308 https://example.com$request_uri;   # permanent, pertahankan method

# Kode lain
return 404;
return 403;
return 410;    # Gone — resource dihapus permanen

rewrite — transformasi URL

nginx
# Syntax: rewrite regex replacement [flag];
# Flag: last (proses ulang), break (stop), redirect (302), permanent (301)

# Hapus trailing slash (kecuali root)
rewrite ^/(.+)/$ /$1 permanent;

# Tambahkan trailing slash ke direktori
rewrite ^/blog$ /blog/ permanent;

# Ubah format URL
rewrite ^/posts/(\d+)/(.*)$ /article?id=$1&slug=$2 last;

# Versi lama ke baru
rewrite ^/old-path/(.*)$ /new-path/$1 permanent;

Custom error pages

nginx
error_page 404              /404.html;
error_page 500 502 503 504  /50x.html;

# Redirect ke URL eksternal
error_page 404 https://example.com/not-found;

# Error page dari backend
error_page 404 = @not_found;
location @not_found {
    proxy_pass http://127.0.0.1:3000/error/404;
}

location = /404.html {
    root /var/www/errors;
    internal;
}

Trailing slash normalization

nginx
# Paksa trailing slash untuk direktori
location /docs {
    return 301 /docs/;
}

# Hapus trailing slash (kecuali root /)
location ~ ^(.+)/$ {
    return 301 $1;
}
11

Logging

Format log, conditional logging, dan manajemen file log.

Format log custom

nginx
# Di konteks http {}

# Format standar
log_format main '$remote_addr - $remote_user [$time_local] '
                '"$request" $status $body_bytes_sent '
                '"$http_referer" "$http_user_agent"';

# Format dengan request time dan upstream info
log_format detailed '$remote_addr - [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    '$request_time $upstream_response_time '
                    '"$http_referer" "$http_user_agent" '
                    '"$http_x_forwarded_for"';

# Format JSON — untuk log aggregation (ELK, Loki, dll.)
log_format json_log escape=json
  '{'
    '"time":"$time_iso8601",'
    '"remote_addr":"$remote_addr",'
    '"method":"$request_method",'
    '"uri":"$uri",'
    '"args":"$args",'
    '"status":$status,'
    '"bytes_sent":$body_bytes_sent,'
    '"request_time":$request_time,'
    '"upstream_time":"$upstream_response_time",'
    '"referrer":"$http_referer",'
    '"user_agent":"$http_user_agent"'
  '}';

access_log /var/log/nginx/access.log     main;
access_log /var/log/nginx/json.log       json_log;

Error log levels

nginx
# Level: debug | info | notice | warn | error | crit | alert | emerg
error_log /var/log/nginx/error.log warn;         # production
error_log /var/log/nginx/error.log debug;        # troubleshooting

# Nonaktifkan log per-location
location /health {
    access_log off;
    return 200 "ok";
}

Conditional logging — filter 2xx

nginx
# Di konteks http {}
map $status $loggable {
    ~^[23]  0;    # jangan log 2xx dan 3xx
    default 1;    # log 4xx dan 5xx
}

# Di konteks server/location {}
access_log /var/log/nginx/error_only.log main if=$loggable;

# Jangan log health check
map $request_uri $log_health {
    "~*/health$"  0;
    default        1;
}
access_log /var/log/nginx/access.log main if=$log_health;

Logrotate config

nginx
# /etc/logrotate.d/nginx
/var/log/nginx/*.log {
    daily
    missingok
    rotate 14
    compress
    delaycompress
    notifempty
    create 0640 www-data adm
    sharedscripts
    postrotate
        nginx -s reopen 2>/dev/null || true
    endscript
}
12

Performance Tuning

Optimasi worker, koneksi, buffer, dan I/O.

Worker dan koneksi

nginx
# Di konteks main
worker_processes auto;              # satu worker per CPU core
worker_rlimit_nofile 65535;         # naikkan batas open file descriptor

events {
    worker_connections 4096;        # maks koneksi per worker
    use epoll;                      # I/O event model terbaik untuk Linux
    multi_accept on;                # terima semua koneksi baru sekaligus
}

Keepalive dan timeout

nginx
http {
    keepalive_timeout     65;       # timeout idle keepalive connection (detik)
    keepalive_requests    1000;     # maks request per keepalive connection
    keepalive_time        1h;       # durasi maksimal keepalive connection

    send_timeout          60s;      # timeout antar dua operasi write
    client_header_timeout 10s;      # timeout baca request header
    client_body_timeout   10s;      # timeout baca request body
    reset_timedout_connection on;   # reset koneksi yang timeout (bebaskan memory)
}

Sendfile dan buffer

nginx
http {
    sendfile    on;         # transfer file langsung kernel-to-kernel (bypass userspace)
    tcp_nopush  on;         # kirim header + awal body dalam satu TCP packet
    tcp_nodelay on;         # nonaktifkan Nagle algorithm (cocok untuk keepalive)

    # Buffer ukuran request client
    client_body_buffer_size  128k;
    client_max_body_size     10m;   # maks ukuran upload (default 1m)
    client_header_buffer_size 1k;
    large_client_header_buffers 4 8k;

    # Buffer output
    output_buffers  2 32k;
    postpone_output 1460;
}

Variabel yang berguna untuk debugging

nginx
$request_time        # total waktu proses request (detik)
$upstream_response_time  # waktu response backend
$upstream_addr           # alamat backend yang digunakan
$upstream_status         # status code dari backend
$upstream_cache_status   # cache: HIT / MISS / BYPASS / EXPIRED
$ssl_protocol            # TLSv1.2 / TLSv1.3
$ssl_cipher              # cipher yang dipakai
$request_id              # unique request ID (NGINX 1.11.0+)
$connection              # nomor koneksi
$bytes_sent              # total bytes dikirim ke client
13

Snippet Siap Pakai

Konfigurasi lengkap untuk use-case umum — copy dan sesuaikan.

Reverse proxy ke Node.js / Next.js

nginx
server {
    listen 80;
    server_name app.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name app.example.com;

    ssl_certificate     /etc/letsencrypt/live/app.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;

    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 Connection        "";
        proxy_read_timeout 60s;
    }

    # Static assets dengan cache panjang
    location /_next/static/ {
        proxy_pass http://127.0.0.1:3000;
        expires 1y;
        add_header Cache-Control "public, immutable";
        access_log off;
    }

    location /favicon.ico {
        proxy_pass http://127.0.0.1:3000;
        access_log off;
    }
}

SPA (React/Vue/Angular) + API proxy

nginx
server {
    listen 443 ssl http2;
    server_name spa.example.com;

    ssl_certificate     /etc/letsencrypt/live/spa.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/spa.example.com/privkey.pem;

    root /var/www/spa/dist;
    index index.html;

    # Gzip
    gzip on;
    gzip_types text/plain text/css application/javascript application/json;

    # API — proxy ke backend
    location /api/ {
        proxy_pass http://127.0.0.1:8080/;
        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;
        limit_req zone=req_limit burst=30 nodelay;
    }

    # Static assets dengan hash — cache agresif
    location ~* \.[0-9a-f]{8}\.(js|css|woff2)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
        access_log off;
    }

    # SPA fallback
    location / {
        try_files $uri $uri/ /index.html;
    }
}

Load balancer multi-backend

nginx
upstream api_pool {
    least_conn;
    server 10.0.0.1:3000 max_fails=3 fail_timeout=30s;
    server 10.0.0.2:3000 max_fails=3 fail_timeout=30s;
    server 10.0.0.3:3000 max_fails=3 fail_timeout=30s backup;
    keepalive 32;
}

server {
    listen 443 ssl http2;
    server_name api.example.com;

    ssl_certificate     /etc/letsencrypt/live/api.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;

    limit_req_zone $binary_remote_addr zone=api_limit:10m rate=100r/s;

    location / {
        proxy_pass http://api_pool;
        proxy_http_version 1.1;
        proxy_set_header Connection     "";
        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_connect_timeout 5s;
        proxy_read_timeout    30s;
        limit_req zone=api_limit burst=50 nodelay;
    }

    location /health {
        access_log off;
        return 200 "ok";
        add_header Content-Type text/plain;
    }
}