# Install GeoIP module and database
sudo apt-get install nginx-module-geoip geoip-database
Add to /etc/nginx/nginx.conf:
# Load GeoIP module
load_module modules/ngx_http_geoip_module.so;
http {
# GeoIP configuration
geoip_country /usr/share/GeoIP/GeoIP.dat;
# Create variables for blocked countries
map $geoip_country_code $allowed_country {
default yes;
CN no; # Block China
RU no; # Block Russia
}
}
Add to your server block:
server {
# ... other configurations ...
# Country blocking
if ($allowed_country = no) {
return 403;
}
# Custom error page
error_page 403 /blocked.html;
location = /blocked.html {
root /usr/share/nginx/html;
internal;
}
}
Add to nginx.conf or include in http block:
# Rate limiting zones
limit_req_zone $binary_remote_addr zone=global:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=api:10m rate=5r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;
# Limit concurrent connections
limit_conn_zone $binary_remote_addr zone=addr:10m;
server {
# Global rate limiting
limit_req zone=global burst=20 nodelay;
limit_conn addr 10;
# API rate limiting
location /api/ {
limit_req zone=api burst=10 nodelay;
limit_conn addr 5;
# Add rate limit headers
add_header X-RateLimit-Limit 100;
add_header X-RateLimit-Remaining $remaining;
add_header X-RateLimit-Reset $time_local;
}
# Login rate limiting
location ~ /(wp-login\.php|administrator|login) {
limit_req zone=login burst=3 nodelay;
limit_conn addr 3;
}
}
Add to your server block:
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" 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'; style-src 'self' 'unsafe-inline';" always;
add_header Permissions-Policy "geolocation=(), midi=(), sync-xhr=(), microphone=(), camera=(), magnetometer=(), gyroscope=(), fullscreen=(self), payment=()";
# CORS headers
add_header Access-Control-Allow-Origin "https://your-domain.com" always;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS, PUT, DELETE" always;
add_header Access-Control-Allow-Headers "Authorization, Content-Type" always;
For PHP-FPM with Nginx:
# PHP-FPM configuration
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
# Security headers
fastcgi_hide_header X-Powered-By;
fastcgi_hide_header X-CF-Powered-By;
# Timeout settings
fastcgi_read_timeout 60s;
fastcgi_send_timeout 60s;
fastcgi_connect_timeout 60s;
}
Add to your server block:
# Deny access to hidden files
location ~ /\. {
deny all;
}
# Protect configuration files
location ~ \.(ini|log|conf)$ {
deny all;
}
# Protect uploads directory
location /uploads {
location ~ \.php$ {
deny all;
}
}
# Protect sensitive directories
location ~ ^/(config|vendor|logs|temp)/ {
deny all;
}
# WordPress protection
location ~* wp-config.php {
deny all;
}
# Joomla protection
location ~* configuration.php {
deny all;
}
# PHP SaaS protection
location ~ ^/api/ {
# API specific rules
if ($request_method !~ ^(GET|POST|PUT|DELETE|OPTIONS)$) {
return 405;
}
# Verify JWT token
if ($http_authorization = "") {
return 401;
}
}
# Install ModSecurity and Nginx connector
sudo apt-get install libmodsecurity3 libmodsecurity-dev
git clone --depth 1 https://github.com/SpiderLabs/ModSecurity-nginx.git
wget http://nginx.org/download/nginx-1.18.0.tar.gz
tar zxvf nginx-1.18.0.tar.gz
cd nginx-1.18.0
./configure --with-compat --add-dynamic-module=../ModSecurity-nginx
make modules
sudo cp objs/ngx_http_modsecurity_module.so /usr/share/nginx/modules/
Add to nginx.conf:
load_module modules/ngx_http_modsecurity_module.so;
http {
modsecurity on;
modsecurity_rules_file /etc/nginx/modsecurity/main.conf;
}
Create /etc/nginx/modsecurity/main.conf:
# Basic configuration
SecRuleEngine On
SecRequestBodyAccess On
SecRequestBodyLimit 13107200
SecRequestBodyNoFilesLimit 131072
SecResponseBodyAccess On
SecResponseBodyLimit 524288
# Include OWASP Core Rule Set
Include /etc/nginx/modsecurity/owasp-crs/crs-setup.conf
Include /etc/nginx/modsecurity/owasp-crs/rules/*.conf
# Custom rules
SecRule REQUEST_HEADERS:Content-Type "text/html" \
"id:1234,phase:1,deny,status:403,msg:'HTML content type not allowed'"
# API protection
SecRule REQUEST_URI "@beginsWith /api/" \
"chain,phase:1,id:4000,deny,status:401,msg:'API authentication required'"
SecRule REQUEST_HEADERS:Authorization "^$"
# Rate limiting
SecRule &ARGS "@eq 0" "phase:2,id:4100,nolog,pass"
SecRule REQUEST_URI "@beginsWith /api/" \
"phase:1,id:4101,nolog,pass,setvar:ip.api_count=+1,expirevar:ip.api_count=60"
SecRule IP:API_COUNT "@gt 100" \
"phase:1,id:4102,deny,status:429,msg:'API rate limit exceeded'"
Create /etc/fail2ban/filter.d/nginx-auth.conf:
[Definition]
failregex = ^<HOST> .* "(?:GET|POST) .*(?:/wp-login\.php|/administrator|/login).*" (?:401|403)
^<HOST> .* "(?:GET|POST|PUT|DELETE) /api/.*" (?:401|403|429)
ignoreregex =
Add to /etc/fail2ban/jail.local:
[nginx-auth]
enabled = true
filter = nginx-auth
logpath = /var/log/nginx/access.log
maxretry = 5
findtime = 300
bantime = 3600
Add to server block:
# SSL configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
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:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# HSTS
add_header Strict-Transport-Security "max-age=63072000" always;
# Monitor Nginx access logs
tail -f /var/log/nginx/access.log | grep "403\|401\|429"
# Monitor error logs
tail -f /var/log/nginx/error.log
# Monitor ModSecurity logs
tail -f /var/log/modsec_audit.log
# Monitor Fail2ban logs
tail -f /var/log/fail2ban.log
After implementing these measures:
- Test rate limiting:
# Test API rate limits
for i in {1..110}; do
curl -H "Authorization: Bearer test" https://your-domain.com/api/endpoint
done
# Test ModSecurity
curl -H "Content-Type: text/html" https://your-domain.com/api/endpoint
-
Test country blocking using a VPN
-
Test SSL configuration:
curl https://www.ssllabs.com/ssltest/analyze.html?d=your-domain.com
- Always backup configuration files before making changes
- Test in staging first
- Monitor logs for false positives
- Keep modules and rules updated
- Use strong SSL/TLS configuration
- Implement proper authentication
- Regular security audits
- DDoS protection
- Regular backups
- Access control
Need help? Create an issue or contact your system administrator.