1. Rate Limiting with limit_req and limit_conn
Rate limiting protects the server from abuse, brute-force attacks, and DDoS by limiting the number of requests from a single client.
1.1. Rate Limiting Basics (limit_req)
http { # Define rate limit zone # $binary_remote_addr: Client IP address (binary format, saves memory) # zone=mylimit:10m: Zone name "mylimit", 10MB memory (~160,000 IPs) # rate=10r/s: 10 requests per second limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;server { listen 80; server_name example.com; location / { # Apply rate limit limit_req zone=mylimit; proxy_pass http://backend; } }
}
Rate values:
# Per second limit_req_zone $binary_remote_addr zone=persec:10m rate=1r/s; # 1 req/sec limit_req_zone $binary_remote_addr zone=persec:10m rate=10r/s; # 10 req/sec limit_req_zone $binary_remote_addr zone=persec:10m rate=100r/s; # 100 req/secPer minute
limit_req_zone $binary_remote_addr zone=permin:10m rate=30r/m; # 30 req/min limit_req_zone $binary_remote_addr zone=permin:10m rate=120r/m; # 120 req/min
Very restrictive (login pages)
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m; # 5 req/min
1.2. Burst và Nodelay
http { limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;server { location / { # Allow burst of 20 requests limit_req zone=mylimit burst=20; # Without nodelay: excess requests delayed # With nodelay: excess requests processed immediately # limit_req zone=mylimit burst=20 nodelay; proxy_pass http://backend; } }
}
Burst explained:
rate=10r/s burst=20
Request pattern:
Time 0s: 30 requests arrive simultaneously
Without burst:
- Request 1-10: Processed immediately
- Request 11-30: Rejected (503 error)
With burst=20:
- Request 1-10: Processed immediately
- Request 11-20: Queued (delayed)
- Request 21-30: Rejected
With burst=20 nodelay:
-
Request 1-30: All processed immediately
-
Client "borrows" from future quota
Next 20 seconds: Client blocked (paying back debt)
1.3. Multiple Rate Limit Zones
http {General API rate limit
limit_req_zone $binary_remote_addr zone=api:10m rate=100r/s;
Login rate limit (strict)
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
Search rate limit
limit_req_zone $binary_remote_addr zone=search:10m rate=10r/s;
Download rate limit
limit_req_zone $binary_remote_addr zone=download:10m rate=2r/s;
server { listen 80; server_name example.com;
# General pages location / { limit_req zone=api burst=50 nodelay; proxy_pass http://backend; } # Login endpoint (very strict) location /api/login { limit_req zone=login burst=3 nodelay; proxy_pass http://backend; } # Search endpoint location /api/search { limit_req zone=search burst=20 nodelay; proxy_pass http://backend; } # Download files location /downloads/ { limit_req zone=download burst=5; root /var/www/downloads; }
} }
1.4. Connection Limiting (limit_conn)
http {Limit concurrent connections per IP
limit_conn_zone $binary_remote_addr zone=addr:10m;
Limit concurrent connections per server
limit_conn_zone $server_name zone=perserver:10m;
server { listen 80; server_name example.com;
# Max 10 concurrent connections per IP limit_conn addr 10; # Max 1000 concurrent connections to this server limit_conn perserver 1000; location /downloads/ { # Stricter limit for downloads limit_conn addr 2; root /var/www/downloads; }
} }
1.5. Custom Rate Limit Keys
http {Rate limit by API key
limit_req_zone $http_x_api_key zone=apikey:10m rate=1000r/s;
Rate limit by cookie (user-based)
limit_req_zone $cookie_user_id zone=userid:10m rate=50r/s;
Rate limit by URI
limit_req_zone $request_uri zone=uri:10m rate=10r/s;
Combined key (IP + User-Agent)
map $binary_remote_addr$http_user_agent $limit_key { default $binary_remote_addr$http_user_agent; } limit_req_zone $limit_key zone=combined:10m rate=20r/s;
server { location /api/ { # Apply appropriate limit limit_req zone=apikey burst=100 nodelay; proxy_pass http://backend; } } }
1.6. Rate Limit Status Codes
http { limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;Custom status code for rate limit
limit_req_status 429; # Default: 503
server { location / { limit_req zone=mylimit burst=20 nodelay;
# Custom error page error_page 429 /rate_limit.html; proxy_pass http://backend; } location = /rate_limit.html { internal; root /var/www/errors; }
} }
1.7. Bypass Rate Limiting
http {Whitelist certain IPs
geo $limit { default 1; 10.0.0.0/8 0; # Internal network 192.168.1.100 0; # Admin IP }
map $limit $limit_key { 0 ""; 1 $binary_remote_addr; }
limit_req_zone $limit_key zone=mylimit:10m rate=10r/s;
server { location / { limit_req zone=mylimit burst=20 nodelay; proxy_pass http://backend; } } }
1.8. Complete Rate Limiting Example
http {Define multiple zones for different purposes
General API - moderate limit
limit_req_zone $binary_remote_addr zone=api:10m rate=100r/s;
Authentication - very strict
limit_req_zone $binary_remote_addr zone=auth:10m rate=5r/m;
Search - medium limit
limit_req_zone $binary_remote_addr zone=search:10m rate=10r/s;
File uploads - strict
limit_req_zone $binary_remote_addr zone=upload:10m rate=2r/m;
Connection limits
limit_conn_zone $binary_remote_addr zone=addr:10m; limit_conn_zone $server_name zone=perserver:10m;
Custom status code
limit_req_status 429; limit_conn_status 429;
Log rate limit events
limit_req_log_level warn;
server { listen 443 ssl http2; server_name api.example.com;
# Global connection limit limit_conn addr 20; limit_conn perserver 10000; # General API endpoints location /api/ { limit_req zone=api burst=200 nodelay; proxy_pass http://backend; proxy_set_header X-Real-IP $remote_addr; } # Authentication endpoints (strict) location ~ ^/api/(login|register|reset-password) { limit_req zone=auth burst=3; proxy_pass http://backend; } # Search endpoint location /api/search { limit_req zone=search burst=30 nodelay; proxy_pass http://backend; } # File upload (very strict) location /api/upload { limit_req zone=upload burst=1; limit_conn addr 1; client_max_body_size 100M; proxy_pass http://backend; } # Rate limit error page error_page 429 /429.html; location = /429.html { internal; default_type application/json; return 429 '{"error":"Too many requests","retry_after":"60"}'; }
} }
2. Chặn IP với deny/allow
2.1. Basic IP Blocking
server { listen 80; server_name example.com;Block specific IP
deny 192.168.1.100;
Block IP range
deny 192.168.1.0/24;
Block specific IPs
deny 10.0.0.5; deny 10.0.0.10;
Allow all others
allow all;
location / { root /var/www/html; } }
2.2. Whitelist Approach
server { listen 80; server_name admin.example.com;Allow specific IPs only
allow 192.168.1.100; allow 10.0.0.0/8; allow 2001:db8::/32; # IPv6
Deny everyone else
deny all;
location / { root /var/www/admin; } }
2.3. Location-specific Blocking
server { listen 80; server_name example.com;Public access
location / { root /var/www/html; }
Admin area - restricted
location /admin/ { allow 192.168.1.0/24; allow 10.0.0.100; deny all;
proxy_pass http://admin_backend;}
API - whitelist only
location /api/ { allow 203.0.113.0/24; deny all;
proxy_pass http://api_backend;
} }
2.4. Geo-based Blocking
http {Define geo blocks
geo $allowed_country { default no;
# US IP ranges 192.0.2.0/24 yes; 198.51.100.0/24 yes; # EU IP ranges 203.0.113.0/24 yes;}
server { listen 80; server_name example.com;
if ($allowed_country = no) { return 403 "Access denied from your location"; } location / { root /var/www/html; }
} }
2.5. Block Bad Bots
http {Define bad bots
map $http_user_agent $bad_bot { default 0; ~malicious 1; ~scrapy 1; ~crawler 1; ~spider 1; ~bot 1; ~curl 1; ~*wget 1; }
server { listen 80; server_name example.com;
if ($bad_bot) { return 403 "Forbidden"; } location / { root /var/www/html; }
} }
2.6. Dynamic IP Blocking
Create blocklist file:
# /etc/nginx/blockips.conf
deny 192.168.1.50;
deny 10.0.0.25;
deny 203.0.113.100;
Include in Nginx:
http {Include blocklist
include /etc/nginx/blockips.conf;
server { listen 80; server_name example.com;
location / { root /var/www/html; }
} }
Script để block IP:
#!/bin/bashblock_ip.sh
IP=$1 BLOCKLIST="/etc/nginx/blockips.conf"
if [ -z "$IP" ]; then echo "Usage: $0 <IP_ADDRESS>" exit 1 fi
Add IP to blocklist
echo "deny $IP;" >> $BLOCKLIST
Reload Nginx
nginx -t && systemctl reload nginx
echo "Blocked IP: $IP"
2.7. Fail2Ban Integration
Install Fail2Ban:
sudo apt install fail2ban
Configure Fail2Ban for Nginx:
# /etc/fail2ban/jail.local [nginx-http-auth] enabled = true port = http,https filter = nginx-http-auth logpath = /var/log/nginx/error.log maxretry = 3 bantime = 3600[nginx-limit-req] enabled = true port = http,https filter = nginx-limit-req logpath = /var/log/nginx/error.log maxretry = 10 bantime = 3600
[nginx-badbots] enabled = true port = http,https filter = nginx-badbots logpath = /var/log/nginx/access.log maxretry = 2 bantime = 86400
Create filter:
# /etc/fail2ban/filter.d/nginx-limit-req.conf
[Definition]
failregex = limiting requests, excess: .* by zone .*, client: <HOST>
ignoreregex =
Restart Fail2Ban:
sudo systemctl restart fail2ban
sudo fail2ban-client status nginx-limit-req
3. Basic Authentication
3.1. Setup Basic Auth
Create password file:
# Install htpasswd utility sudo apt install apache2-utilsCreate user
sudo htpasswd -c /etc/nginx/.htpasswd admin
Add more users
sudo htpasswd /etc/nginx/.htpasswd user2
Set permissions
sudo chmod 644 /etc/nginx/.htpasswd
Configure Nginx:
server { listen 80; server_name example.com;location /admin/ { # Enable basic auth auth_basic "Restricted Area"; auth_basic_user_file /etc/nginx/.htpasswd; root /var/www/admin; }
}
3.2. Location-specific Auth
server { listen 80; server_name example.com;# Public area location / { root /var/www/html; } # Protected admin area location /admin/ { auth_basic "Admin Area"; auth_basic_user_file /etc/nginx/.htpasswd; root /var/www/admin; } # Protected API location /api/ { auth_basic "API Access"; auth_basic_user_file /etc/nginx/.htpasswd_api; proxy_pass http://backend; }
}
3.3. Conditional Authentication
http { # Whitelist IPs that don't need auth geo $authentication { default "Restricted"; 192.168.1.0/24 "off"; 10.0.0.0/8 "off"; }server { listen 80; location /admin/ { auth_basic $authentication; auth_basic_user_file /etc/nginx/.htpasswd; root /var/www/admin; } }
}
3.4. Multiple Auth Methods
server { listen 80; server_name example.com;location /admin/ { # Require IP whitelist AND password satisfy all; # IP whitelist allow 192.168.1.0/24; deny all; # Basic auth auth_basic "Admin Area"; auth_basic_user_file /etc/nginx/.htpasswd; root /var/www/admin; } location /staff/ { # Require IP whitelist OR password satisfy any; allow 192.168.1.0/24; deny all; auth_basic "Staff Area"; auth_basic_user_file /etc/nginx/.htpasswd; root /var/www/staff; }
}
3.5. JWT Authentication
http { # Verify JWT token map $http_authorization $jwt_valid { default 0; ~^Bearer\s+([A-Za-z0-9-=]+.[A-Za-z0-9-=]+.?[A-Za-z0-9-_.+/=]) 1; }server { listen 80; location /api/ { if ($jwt_valid = 0) { return 401 '{"error":"Invalid or missing token"}'; } # Forward token to backend for validation proxy_pass http://backend; proxy_set_header Authorization $http_authorization; } }
}
4. ModSecurity WAF Integration
ModSecurity là Web Application Firewall (WAF) bảo vệ khỏi common attacks.
4.1. Install ModSecurity
# Ubuntu/Debian sudo apt update sudo apt install libmodsecurity3 libmodsecurity-devClone connector
cd /opt sudo git clone https://github.com/SpiderLabs/ModSecurity-nginx.git
Get Nginx version
nginx -v
Download Nginx source (same version)
cd /opt wget http://nginx.org/download/nginx-1.24.0.tar.gz tar -xzf nginx-1.24.0.tar.gz cd nginx-1.24.0
Compile with ModSecurity module
./configure --add-dynamic-module=/opt/ModSecurity-nginx make modules
Copy module
sudo cp objs/ngx_http_modsecurity_module.so /etc/nginx/modules/
Load module in nginx.conf
sudo nano /etc/nginx/nginx.conf
Add at top:
load_module modules/ngx_http_modsecurity_module.so;
4.2. Configure ModSecurity
# Create config directory sudo mkdir /etc/nginx/modsec cd /etc/nginx/modsecDownload OWASP Core Rule Set
sudo git clone https://github.com/coreruleset/coreruleset.git
Copy config files
sudo cp /opt/ModSecurity/modsecurity.conf-recommended modsecurity.conf sudo cp /opt/ModSecurity/unicode.mapping .
Edit config
sudo nano modsecurity.conf
Change:
SecRuleEngine On # Enable rules SecAuditLog /var/log/modsec_audit.log
4.3. Enable in Nginx
http { # Load ModSecurity module load_module modules/ngx_http_modsecurity_module.so;server { listen 80; server_name example.com; # Enable ModSecurity modsecurity on; modsecurity_rules_file /etc/nginx/modsec/modsecurity.conf; location / { proxy_pass http://backend; } }
}
4.4. OWASP Core Rule Set
# Main config file sudo nano /etc/nginx/modsec/main.confAdd:
Include /etc/nginx/modsec/modsecurity.conf Include /etc/nginx/modsec/coreruleset/crs-setup.conf Include /etc/nginx/modsec/coreruleset/rules/*.conf
Update Nginx config:
server { listen 80; server_name example.com;modsecurity on; modsecurity_rules_file /etc/nginx/modsec/main.conf; location / { proxy_pass http://backend; }
}
4.5. Custom ModSecurity Rules
# /etc/nginx/modsec/custom_rules.confBlock SQL injection attempts
SecRule ARGS "@detectSQLi"
"id:1001,phase:2,deny,status:403,msg:'SQL Injection Detected'"Block XSS attempts
SecRule ARGS "@detectXSS"
"id:1002,phase:2,deny,status:403,msg:'XSS Detected'"Block file upload exploits
SecRule FILES "@rx .(?:php|exe|sh)$"
"id:1003,phase:2,deny,status:403,msg:'Dangerous file upload'"Rate limiting per IP
SecRule IP:REQUEST_COUNT "@gt 100"
"id:1004,phase:1,deny,status:429,msg:'Rate limit exceeded'"
5. Chống DDoS Cơ bản
5.1. Connection Limits
http { # Limit connections per IP limit_conn_zone $binary_remote_addr zone=addr:10m;# Limit request rate limit_req_zone $binary_remote_addr zone=req:10m rate=10r/s; server { listen 80; server_name example.com; # Apply limits limit_conn addr 10; limit_req zone=req burst=20 nodelay; location / { proxy_pass http://backend; } }
}
5.2. Timeout Protection
http { # Short timeouts to free resources quickly client_body_timeout 10s; client_header_timeout 10s; send_timeout 10s;# Limit request body size client_max_body_size 1M; server { listen 80; location / { proxy_pass http://backend; # Backend timeouts proxy_connect_timeout 5s; proxy_send_timeout 10s; proxy_read_timeout 10s; } }
}
5.3. Block Attack Patterns
http { # Block bad methods map $request_method $bad_method { default 0; ~*^(TRACE|DELETE|PUT) 1; }# Block suspicious URIs map $request_uri $bad_uri { default 0; ~*\.\./\.\. 1; # Directory traversal ~*\.(bash|git|svn|env|log) 1; ~*\.(php|asp|aspx|jsp)\. 1; # Double extensions } # Block bad referers map $http_referer $bad_referer { default 0; ~*porn 1; ~*casino 1; ~*viagra 1; } server { listen 80; if ($bad_method) { return 405; } if ($bad_uri) { return 403; } if ($bad_referer) { return 403; } location / { proxy_pass http://backend; } }
}
5.4. SYN Flood Protection
# System-level protection sudo nano /etc/sysctl.confAdd:
net.ipv4.tcp_syncookies = 1 net.ipv4.tcp_max_syn_backlog = 2048 net.ipv4.tcp_synack_retries = 2 net.ipv4.tcp_syn_retries = 2
Apply
sudo sysctl -p
5.5. Slowloris Protection
http { # Protect against slowloris client_body_timeout 10s; client_header_timeout 10s; send_timeout 10s;# Limit header size large_client_header_buffers 2 1k; # Request body limits client_body_buffer_size 1k; client_max_body_size 1M; server { listen 80; location / { proxy_pass http://backend; } }
}
5.6. Complete DDoS Protection
http { # Connection limits limit_conn_zone $binary_remote_addr zone=addr:10m; limit_conn_zone $server_name zone=perserver:10m;# Rate limits limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s; limit_req_zone $binary_remote_addr zone=strict:10m rate=1r/s; # Timeouts client_body_timeout 10s; client_header_timeout 10s; send_timeout 10s; keepalive_timeout 30s; # Size limits client_body_buffer_size 1k; client_max_body_size 1M; client_header_buffer_size 1k; large_client_header_buffers 2 1k; # Bad patterns map $request_method $bad_method { default 0; ~*^(TRACE|DELETE|PUT|PATCH) 1; } map $http_user_agent $bad_bot { default 0; "" 1; # Empty user agent ~*bot 1; ~*crawler 1; ~*spider 1; } server { listen 80; server_name example.com; # Connection limits limit_conn addr 20; limit_conn perserver 10000; # Rate limits limit_req zone=general burst=50 nodelay; # Block bad requests if ($bad_method) { return 405; } if ($bad_bot) { return 403; } # General content location / { proxy_pass http://backend; proxy_connect_timeout 5s; proxy_send_timeout 10s; proxy_read_timeout 10s; } # Sensitive endpoints (stricter) location /api/admin/ { limit_req zone=strict burst=5; limit_conn addr 5; allow 192.168.1.0/24; deny all; proxy_pass http://backend; } }
}
6. Secure Headers
6.1. X-Frame-Options
Prevents clickjacking attacks.
server { listen 443 ssl http2; server_name example.com;# Prevent framing (clickjacking) add_header X-Frame-Options "SAMEORIGIN" always; # Or completely deny framing: # add_header X-Frame-Options "DENY" always; # Or allow specific origin: # add_header X-Frame-Options "ALLOW-FROM https://trusted.com" always;
}
6.2. X-Content-Type-Options
Prevents MIME-type sniffing.
server { listen 443 ssl http2;# Prevent MIME sniffing add_header X-Content-Type-Options "nosniff" always;
}
6.3. X-XSS-Protection
Enables browser XSS filter.
server { listen 443 ssl http2;# Enable XSS filter add_header X-XSS-Protection "1; mode=block" always;
}
6.4. Referrer-Policy
Controls referrer information.
server { listen 443 ssl http2;# Referrer policy add_header Referrer-Policy "no-referrer-when-downgrade" always; # Options: # no-referrer - Never send referrer # no-referrer-when-downgrade - Send on HTTPS->HTTPS, not HTTPS->HTTP # origin - Send only origin # origin-when-cross-origin - Full URL for same origin, origin for cross-origin # same-origin - Send only for same origin # strict-origin - Send origin for HTTPS->HTTPS # strict-origin-when-cross-origin - Recommended # unsafe-url - Always send full URL
}
6.5. Content-Security-Policy (CSP)
Powerful defense against XSS and injection attacks.
server { listen 443 ssl http2; server_name example.com;# Basic CSP add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self' https://api.example.com; frame-ancestors 'self';" always;
}
CSP directives explained:
# Strict CSP (most secure) add_header Content-Security-Policy " default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self'; font-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; " always;Moderate CSP (allows CDNs)
add_header Content-Security-Policy " default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://code.jquery.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self' https://api.example.com; frame-ancestors 'self'; " always;
Report-only mode (testing)
add_header Content-Security-Policy-Report-Only " default-src 'self'; report-uri /csp-report; " always;
6.6. Permissions-Policy
Controls browser features.
server { listen 443 ssl http2;# Permissions policy (formerly Feature-Policy) add_header Permissions-Policy " geolocation=(self), microphone=(), camera=(), payment=(), usb=(), magnetometer=(), gyroscope=(), accelerometer=() " always;
}
6.7. CORS Headers
server { listen 443 ssl http2; server_name api.example.com;location /api/ { # CORS headers add_header Access-Control-Allow-Origin "https://app.example.com" always; add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always; add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-Requested-With" always; add_header Access-Control-Allow-Credentials "true" always; add_header Access-Control-Max-Age "3600" always; # Handle preflight OPTIONS requests if ($request_method = OPTIONS) { return 204; } proxy_pass http://backend; }
}
Wildcard CORS (use with caution):
location /api/ { add_header Access-Control-Allow-Origin "*" always; add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always; add_header Access-Control-Allow-Headers "Content-Type" always;if ($request_method = OPTIONS) { return 204; } proxy_pass http://backend;
}
6.8. Complete Security Headers
server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name example.com;# SSL configuration 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; # Security headers add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; 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; add_header Content-Security-Policy " default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self' https://api.example.com; frame-ancestors 'self'; base-uri 'self'; form-action 'self'; " always; add_header Permissions-Policy " geolocation=(self), microphone=(), camera=(), payment=(), usb=() " always; # Hide Nginx version server_tokens off; # Additional security more_clear_headers Server; more_clear_headers X-Powered-By; root /var/www/html; index index.html; location / { try_files $uri $uri/ =404; }
}
7. Complete Security Configuration
7.1. Production-Ready Security Setup
# /etc/nginx/nginx.confuser nginx; worker_processes auto; worker_rlimit_nofile 65535; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid;
events { worker_connections 4096; use epoll; multi_accept on; }
http { include /etc/nginx/mime.types; default_type application/octet-stream;
# Hide version server_tokens off; more_clear_headers Server; # Rate limiting zones limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s; limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m; limit_req_zone $binary_remote_addr zone=api:10m rate=100r/s; # Connection limiting limit_conn_zone $binary_remote_addr zone=addr:10m; limit_conn_zone $server_name zone=perserver:10m; # Status codes limit_req_status 429; limit_conn_status 429; # IP blocklist include /etc/nginx/blockips.conf; # Bad bot blocking map $http_user_agent $bad_bot { default 0; "" 1; ~*bot 1; ~*crawler 1; ~*scraper 1; } # Bad methods map $request_method $bad_method { default 0; ~*^(TRACE|DELETE|PUT) 1; } # Timeouts client_body_timeout 12s; client_header_timeout 12s; send_timeout 10s; keepalive_timeout 65s; # Size limits client_body_buffer_size 128k; client_max_body_size 10M; client_header_buffer_size 1k; large_client_header_buffers 4 8k; # Logging log_format security '$remote_addr - $remote_user [$time_local] ' '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent" ' 'rt=$request_time uct=$upstream_connect_time ' 'uht=$upstream_header_time urt=$upstream_response_time'; access_log /var/log/nginx/access.log security; # Include configs include /etc/nginx/conf.d/*.conf; include /etc/nginx/sites-enabled/*;
}
7.2. Secure Site Configuration
# /etc/nginx/sites-available/example.comUpstream
upstream backend { least_conn; server backend1:8080 max_fails=3 fail_timeout=30s; server backend2:8080 max_fails=3 fail_timeout=30s; keepalive 32; }
HTTP -> HTTPS redirect
server { listen 80; listen [::]:80; server_name example.com www.example.com;
# Block bad bots if ($bad_bot) { return 403; } # Block bad methods if ($bad_method) { return 405; } location /.well-known/acme-challenge/ { root /var/www/html; } location / { return 301 https://example.com$request_uri; }}
Main HTTPS server
server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name example.com;
root /var/www/html; index index.html; # SSL 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_session_cache shared:SSL:10m; ssl_stapling on; ssl_stapling_verify on; # Security headers add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; 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; add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';" always; # Connection limits limit_conn addr 20; limit_conn perserver 10000; # Rate limiting limit_req zone=general burst=50 nodelay; # Block bad requests if ($bad_bot) { return 403; } if ($bad_method) { return 405; } # Main location location / { try_files $uri $uri/ =404; } # API endpoints location /api/ { limit_req zone=api burst=200 nodelay; proxy_pass http://backend/; 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_set_header X-Forwarded-Proto $scheme; } # Login endpoint (strict rate limit) location /api/login { limit_req zone=login burst=3; proxy_pass http://backend/api/login; proxy_http_version 1.1; proxy_set_header Connection ""; } # Admin area (IP whitelist + auth) location /admin/ { satisfy all; allow 192.168.1.0/24; allow 10.0.0.100; deny all; auth_basic "Admin Area"; auth_basic_user_file /etc/nginx/.htpasswd; proxy_pass http://backend/admin/; } # Static assets location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ { expires 1y; add_header Cache-Control "public, immutable"; access_log off; } # Deny hidden files location ~ /\. { deny all; access_log off; log_not_found off; } # Error pages error_page 403 /403.html; error_page 404 /404.html; error_page 429 /429.html; error_page 500 502 503 504 /50x.html;
}
8. Security Testing và Monitoring
8.1. Security Scanners
# Nikto vulnerability scanner nikto -h https://example.comOWASP ZAP
zap-cli quick-scan https://example.com
SSL Labs test
Visit: https://www.ssllabs.com/ssltest/
Security Headers test
Visit: https://securityheaders.com
8.2. Monitor Attacks
#!/bin/bashmonitor_attacks.sh
LOG_FILE="/var/log/nginx/access.log" ERROR_LOG="/var/log/nginx/error.log"
echo "Security Monitoring" echo "=================="
Failed login attempts
echo "Failed Login Attempts:" grep "/api/login" $LOG_FILE | grep -c " 401 "
Rate limit hits
echo "Rate Limit Hits:" grep "limiting requests" $ERROR_LOG | wc -l
Blocked IPs
echo "Blocked IPs:" grep " 403 " $LOG_FILE | awk '{print $1}' | sort | uniq -c | sort -rn | head -10
Suspicious URLs
echo "Suspicious URLs:" grep -E "../|.php.|.env|.git" $LOG_FILE | wc -l
Top 403 URLs
echo "Top 403 URLs:" grep " 403 " $LOG_FILE | awk '{print $7}' | sort | uniq -c | sort -rn | head -10
8.3. Real-time Alerts
#!/bin/bashsecurity_alert.sh
THRESHOLD=100 EMAIL="[email protected]"
Check rate limit hits
RATE_LIMIT_HITS=$(grep "limiting requests" /var/log/nginx/error.log | wc -l)
if [ $RATE_LIMIT_HITS -gt $THRESHOLD ]; then echo "ALERT: $RATE_LIMIT_HITS rate limit hits detected" | mail -s "Security Alert" $EMAIL fi
Check for SQL injection attempts
SQL_ATTEMPTS=$(grep -E "union.*select|concat(|load_file" /var/log/nginx/access.log | wc -l)
if [ $SQL_ATTEMPTS -gt 0 ]; then echo "ALERT: $SQL_ATTEMPTS SQL injection attempts detected" | mail -s "SQL Injection Alert" $EMAIL fi
Summary
In this lesson, you learned:
- ✅ Rate limiting với limit_req và limit_conn
- ✅ IP blocking và whitelisting
- ✅ Basic authentication và JWT
- ✅ ModSecurity WAF integration
- ✅ DDoS protection techniques
- ✅ Secure headers (CSP, X-Frame-Options, CORS)
- ✅ Security testing và monitoring
Security checklist:
- ✅ Rate limiting enabled
- ✅ IP blocklist configured
- ✅ Authentication on sensitive areas
- ✅ All security headers present
- ✅ SSL/TLS properly configured
- ✅ Fail2Ban integrated
- ✅ Regular security audits
- ✅ Monitoring và alerting active
Next lesson: Rewrite và Redirects - regex patterns, return vs rewrite directives, location matching, try_files và conditional redirects để quản lý URL routing effectively.