Skip to content

Instantly share code, notes, and snippets.

@josuamarcelc
Created August 23, 2025 14:09
Show Gist options
  • Select an option

  • Save josuamarcelc/565b007f77659bb8acd73bd4ee1af86b to your computer and use it in GitHub Desktop.

Select an option

Save josuamarcelc/565b007f77659bb8acd73bd4ee1af86b to your computer and use it in GitHub Desktop.
BEST PRACTICE Advanced Security & DDoS Protection ON NGINX WITH CODEIGNITER 3
# Advanced Security & DDoS Protection
# Rate limiting zones
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
limit_req_zone $binary_remote_addr zone=search:10m rate=20r/m;
limit_req_zone $binary_remote_addr zone=general:10m rate=30r/s;
# Connection limiting
limit_conn_zone $binary_remote_addr zone=conn_limit_per_ip:10m;
# Upstream for load balancing (if needed)
upstream xyzid_backend {
server 127.0.0.1:9000;
keepalive 32;
}
# Main server block
server {
listen 80;
listen [::]:80;
server_name xyz.id www.xyz.id;
# 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;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net https://code.jquery.com; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self'; frame-ancestors 'self';" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
# Hide server version
server_tokens off;
# Connection limits
limit_conn conn_limit_per_ip 10;
# Block bad bots
if ($http_user_agent ~* (bot|crawler|spider|scraper)) {
return 403;
}
# Block suspicious requests
if ($request_uri ~* "\.(php|asp|aspx|jsp|cgi)$") {
return 404;
}
# Block common attack patterns
if ($request_uri ~* "(eval|base64|union|select|insert|drop|delete|update|exec|system|shell|cmd|wget|curl)") {
return 444;
}
# Redirect HTTP to HTTPS
return 301 https://$server_name$request_uri;
}
# HTTPS server block
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name xyz.id www.xyz.id;
ssl_certificate /etc/letsencrypt/live/xyz.id/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/xyz.id/privkey.pem;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
# Modern SSL configuration
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;
# HSTS
add_header Strict-Transport-Security "max-age=63072000" always;
# 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;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net https://code.jquery.com; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self'; frame-ancestors 'self';" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
# Hide server version
server_tokens off;
# Connection limits
limit_conn conn_limit_per_ip 10;
# Root directory
root /var/www/xyz.id;
index index.php index.html;
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied any;
gzip_comp_level 6;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml+rss
application/atom+xml
image/svg+xml;
# File upload limits
client_max_body_size 10M;
client_body_timeout 60s;
client_header_timeout 60s;
# Block bad bots
if ($http_user_agent ~* (bot|crawler|spider|scraper)) {
return 403;
}
# Block suspicious requests
if ($request_uri ~* "\.(php|asp|aspx|jsp|cgi)$") {
return 404;
}
# Block common attack patterns
if ($request_uri ~* "(eval|base64|union|select|insert|drop|delete|update|exec|system|shell|cmd|wget|curl)") {
return 444;
}
# Rate limiting for different endpoints
location /admin/ {
limit_req zone=api burst=5 nodelay;
limit_req zone=general burst=10 nodelay;
try_files $uri $uri/ /index.php?$query_string;
}
location /seller/ {
limit_req zone=general burst=15 nodelay;
try_files $uri $uri/ /index.php?$query_string;
}
location /api/ {
limit_req zone=api burst=5 nodelay;
try_files $uri $uri/ /index.php?$query_string;
}
location /search {
limit_req zone=search burst=10 nodelay;
try_files $uri $uri/ /index.php?$query_string;
}
# Static files
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
add_header X-Content-Type-Options "nosniff";
}
# PHP processing
location ~ \.php$ {
limit_req zone=api burst=5 nodelay;
fastcgi_pass xyzid_backend;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
# FastCGI timeouts
fastcgi_read_timeout 300;
fastcgi_connect_timeout 60;
fastcgi_send_timeout 60;
# Security headers for PHP
fastcgi_param HTTP_X_FORWARDED_PROTO https;
fastcgi_param HTTP_X_FORWARDED_SSL on;
}
# Deny access to sensitive files
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
location ~ ~$ {
deny all;
access_log off;
log_not_found off;
}
location ~ \.(htaccess|htpasswd|ini|log|sh|sql|conf)$ {
deny all;
access_log off;
log_not_found off;
}
# Main location block
location / {
limit_req zone=general burst=20 nodelay;
try_files $uri $uri/ /index.php?$query_string;
}
# Error pages
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# Access and error logs
access_log /var/log/nginx/xyz.id.access.log combined buffer=512k flush=1m;
error_log /var/log/nginx/xyz.id.error.log warn;
location ^~ /.well-known/acme-challenge/ {
default_type "text/plain";
root /var/www/xyz.id; # any readable path; certbot overrides when using --nginx
allow all;
# Bypass rate limits / WAF-y rules for ACME
limit_req off;
limit_conn conn_limit_per_ip 0;
add_header X-ACME-Bypass "true" always;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment