Start by tuning global settings for performance and security. That are-
Disable server tokens to prevent attackers from learning details about your NGINX version.
Resolver for DNS lookups.
Define a mapping to generate a CSP nonce based on the request ID.
A second map filters out suspicious User-Agent strings to block common malicious bots.
nginx.conf (Global Part):
# Global settings
worker_processes auto;
events {
worker_connections 1024;
multi_accept on;
}
http {
include mime.types;
default_type application/octet-stream;
client_max_body_size 20M;
# Use a public DNS resolver for upstream lookups
resolver 8.8.8.8 valid=10s;
# Disable NGINX version info in error pages and headers
server_tokens off;
keepalive_timeout 65; # Keeps connections open for reuse up to 65 seconds
sendfile on; # Enable zero-copy file transmission
tcp_nodelay on; # Disable Nagle's algorithm to reduce latency
tcp_nopush on; # Optimize packet transmission (commonly used with sendfile)
gzip on;
gzip_comp_level 5; # Compression level (1-9)
gzip_min_length 256; # Only compress responses larger than 256 bytes
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss;
gzip_vary on;
# Map to generate a nonce for CSP headers based on $request_id
map $request_id $csp_nonce {
"~*" $request_id;
}
# Map to match suspicious or malicious User-Agent strings
# use this carefully as it will block crawlers also
map $http_user_agent $bad_user_agent {
default 0;
~*^$ 1; # Empty User Agent
~*bot 1; # Bots (use caution, some legitimate bots may be blocked)
~*spider 1;
~*crawl 1;
~*[<>]script 1; # Potential XSS attempts
~*(nmap|nikto|wikto|sf|sqlmap|bsqlbf|w3af|acunetix|havij|appscan) 1;
}
# Include additional project configuration
include app.conf;
}
Explanation:
worker_processes/worker_connections: Set up to efficiently handle concurrent connections.
server_tokens off: Prevents disclosing NGINX version details.
Resolver: Ensures proper DNS lookups.
Maps ($csp_nonce, $bad_user_agent): Dynamically generate CSP nonces and block obvious bad bots, mitigating issues like injection (OWASP Injection) and reconnaissance (vulnerable components).
2. SSL/TLS Setup & HTTP to HTTPS Redirection
Securing connections with SSL/TLS is essential. We can use Let’s Encrypt (via Certbot) in production. For internal services or testing, self‑signed certificates may suffice.
A. Using Let’s Encrypt with Certbot
For “example.com” and subdomains (e.g., “app.example.com”), configure an HTTP server block to redirect all traffic to HTTPS, then configure an HTTPS server block.
HTTP to HTTPS Redirect:
server {
listen 80;
server_name example.com *.example.com;
# Permanent redirect to force HTTPS
return 301 https://$host$request_uri;
}
HTTPS Server Block (SSL/TLS with Let’s Encrypt):
server {
listen 443 ssl http2;
server_name example.com www.example.com;
# SSL Certificates provided by Let’s Encrypt
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# Enforce strong TLS protocols and ciphers
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# HSTS: instruct browsers to use HTTPS exclusively for one year, The HSTS header tells browsers to access your site securely, mitigating man-in-the-middle attacks and protocol downgrade attempts.
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# Additional security headers can be added here (see Section 4)
location / {
# Example content or proxy settings (see later sections)
proxy_pass http://backend.com:8080;
}
}
Explanation:
HTTP to HTTPS redirection: Ensures all connections are encrypted, mitigating data eavesdropping (OWASP Cryptographic Failures).
SSL protocols and HSTS: Force modern TLS standards and protect against downgrade attacks.
B. Self-Signed Certificates
For internal or development environments, self-signed certificates can be used.
Security headers help prevent many common attacks. We set up several headers such as X‑Frame‑Options, X‑XSS‑Protection, and a detailed CSP header that also accommodates trusted third‑party services (e.g., Google APIs, CDN providers).
Example Security Headers
# In the server block (see full configuration later)
# Prevent your site from being framed to prevent clickjacking
add_header X-Frame-Options 'deny, nosniff' always;
add_header X-Content-Type-Options nosniff always;
# Enables XSS filtering in supported browsers, this header instructs browsers to block pages when they detect reflected XSS.
add_header "X-XSS-Protection" "1; mode=block" always;
add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Cache-Control "no-cache, no-store, must-revalidate" always;
# Content-Security-Policy allowing trusted services and applying a nonce for inline scripts
add_header Content-Security-Policy "default-src 'self'; \
script-src 'self' 'unsafe-eval' 'nonce-$csp_nonce' https://maps.googleapis.com https://www.google.com https://www.gstatic.com https://cdnjs.cloudflare.com; \
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; \
font-src 'self' data: https://fonts.gstatic.com; \
frame-src 'self' https://www.google.com $allowed_origin; \
connect-src 'self' data: https://*.googleapis.com wss://example.com; \
object-src 'none'; \
img-src 'self' data:; \
frame-ancestors 'self' $allowed_origin; \
worker-src 'self' data: blob:; \
base-uri 'self'; \
form-action 'self'; \
upgrade-insecure-requests;" always;
add_header X-Permitted-Cross-Domain-Policies "none" always;
add_header Feature-Policy "geolocation 'none'; midi 'none'; sync-xhr 'none'; microphone 'none'; camera 'none'; magnetometer 'none'; gyroscope 'none'; fullscreen 'self'; payment 'none'" always;
Explanation:
These headers reduce the risk of clickjacking, XSS, MIME type confusion, and data leakage. The CSP header is tuned to allow scripts and styles from trusted sources (e.g., Google APIs, cdn.jsdelivr.net) and uses a nonce to allow inline scripts only when authorized.
4. Mitigating SQL Injection
Although preventing SQL injection is primarily an application-layer responsibility (using parameterized queries), NGINX can help filter out malicious requests.
server {
listen 443 ssl;
server_name example.com;
location / {
# Block suspicious SQL injection patterns in the query string
if ($query_string ~* "union.*select.*\(") {
return 403;
}
proxy_pass http://backend;
}
}
Why?
This simple pattern-based filter adds an extra layer of protection by rejecting obviously malicious requests, though backend validation remains essential.
4. DDoS Protection & Rate Limiting
Protect your server from abuse and DDoS attacks by limiting the number of requests from a single client.
Rate Limiting Example:
http {
# Define a shared zone for rate limiting by client IP
limit_req_zone $binary_remote_addr zone=ddos_zone:10m rate=10r/s;
...
server {
listen 443 ssl http2;
server_name example.com www.example.com;
location / {
# Apply rate limiting with burst capacity to handle short spikes
limit_req zone=ddos_zone burst=20 nodelay;
proxy_pass http://backend.com:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
}
Explanation:
This configuration limits clients to 10 requests per second (with bursts of up to 20), helping prevent abuse and service disruption (mitigating OWASP’s Insecure Design and Vulnerable Components).
5. Access Restrictions by IP, GeoIP, and User-Agent
Limit access to sensitive endpoints by IP, geographical region, or by filtering malicious user agents.
A. IP-based Restrictions
server {
listen 443 ssl http2;
server_name admin.example.com;
location /admin/ {
# Only allow trusted IP ranges to access the admin area
allow 192.168.1.0/24;
deny all;
proxy_pass http://backend.com:8080/admin/;
}
}
B. User-Agent Filtering
In our global configuration, we mapped suspicious User-Agents to a variable ($bad_user_agent). Then, within the server block, we block those requests:
if ($bad_user_agent) { # bad_user_agent defined in global
return 403;
}
if ($http_user_agent ~* "(?:maliciousBot|badCrawler)") {
return 403;
}
Explanation:
These restrictions help mitigate unauthorized access and reduce attack surfaces (addressing Broken Access Control and Identification Failures).
C. Restricting by GeoIP
(Requires GeoIP module and database.)
http {
geo $allowed_country {
default no;
# Only allow traffic from the US and Canada
US yes;
CA yes;
}
server {
listen 80;
server_name geo.example.com;
location / {
if ($allowed_country = no) {
return 403;
}
proxy_pass http://backend;
}
}
}
Why?
GeoIP restrictions reduce malicious traffic from regions where you do not operate, improving overall security posture.
6. Blocking Sensitive Directories and Files
Prevent access to internal or version control directories that could expose sensitive data.
Explanation:
Blocking these directories prevents attackers from accessing configuration files and repository data, reducing risk of information disclosure.
7. Blocking Bots & Bad Traffic with ModSecurity
ModSecurity is a robust web application firewall (WAF) that integrates with NGINX to block malicious traffic, bots, and a wide range of web attacks (from SQL injection to XSS), further hardening your server against the OWASP Top 10 vulnerabilities. [ We need to install this module ]
server {
listen 80;
server_name secure.example.com;
# Enable ModSecurity with its configuration rules (e.g., OWASP CRS)
modsecurity on;
modsecurity_rules_file /etc/nginx/modsec/main.conf;
location / {
proxy_pass http://backend;
}
}
Why?
ModSecurity, when combined with the OWASP Core Rule Set (CRS), can automatically block many common vulnerabilities and malicious requests, enhancing your server’s security.
8. Proxying to Backend & Handling Subdomains
When using NGINX as a reverse proxy for dynamic content, you may host multiple subdomains. For example, a subdomain like “marine.example.com” can proxy traffic to your backend server.
Subdomain Proxy Example (Marine UI):
server {
listen 443 ssl http2;
server_name marine.example.com;
# Inherit similar SSL and security header settings as the main domain
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 HIGH:!aNULL:!MD5;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Frame-Options 'deny, nosniff' 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 Cache-Control "no-cache, no-store, must-revalidate" always;
# Define allowed_origin for CSP purposes
set $allowed_origin https://example.com;
# Block bad user agents early
if ($bad_user_agent) {
return 403;
}
location / {
proxy_pass http://backend.com:8080/;
proxy_hide_header Server;
# Set a valid Origin header based on our allowed origin
if ($http_origin = "") {
set $origin_value "$allowed_origin";
}
proxy_set_header Origin $origin_value;
}
}
Explanation:
Subdomains: This server block handles “marine.example.com” with similar security settings while proxying requests to “backend.com:8080.”
Allowed Origin: Ensures that CSP and CORS-related headers use a trusted origin.
9. Mitigating OWASP Top 10 Vulnerabilities
Here’s how our configuration helps mitigate common OWASP risks:
Broken Access Control:
Use IP-based restrictions and enforce HTTPS with HSTS.
Backend must also implement authentication/authorization.
Cryptographic Failures:
Enforce strong TLS protocols, secure ciphers, and HSTS.
Injection (SQL, Command, etc.):
Simple query string filters help block obvious injection patterns.
Application-level sanitization remains critical.
Insecure Design:
Layered security with proper headers, rate limiting, and access controls.
Security Misconfiguration:
Regularly update configurations and disable server tokens.
Vulnerable and Outdated Components:
Keep NGINX and modules up to date.
Identification and Authentication Failures:
Enforce HTTPS and restrict sensitive endpoints.
Software and Data Integrity Failures:
Use secure transmission (SSL/TLS) and signed updates.
Security Logging and Monitoring Failures:
Ensure thorough logging and integrate ModSecurity.
Server-Side Request Forgery (SSRF):
Validate upstream requests and restrict access via firewall rules.
10. Final Consolidated Configuration
Below is the final, consolidated configuration incorporating all the suggestions, modified to use “example.com” and “backend.com:8080.” It also considers subdomains and third-party services.