Implementing Security Best Practices
1. Hiding Nginx Version
| Directive | Values | Description |
|---|---|---|
| server_tokens | off |
Hides the Nginx version from error pages and HTTP headers |
| server_tokens | on |
Shows the Nginx version (default behavior) |
Example: Hide Nginx Version
server {
listen 80;
server_name example.com;
# Hide Nginx version
server_tokens off;
location / {
return 200 'Hello World';
}
}
Note: Setting
server_tokens off removes the version information from error pages and HTTP response headers, enhancing security by preventing attackers from identifying the server software and version.
2. Disabling Unwanted Methods
| Method | Description | Configuration | Notes |
|---|---|---|---|
| GET | Retrieve resources | allow_method GET; |
Default allowed method |
| POST | Submit data to the server | allow_method POST; |
Commonly used for form submissions |
| PUT | Update or replace resources | allow_method PUT; |
May be disabled for security |
| DELETE | Remove resources | allow_method DELETE; |
Often restricted in production |
| PATCH | Partially update resources | allow_method PATCH; |
May require specific handling |
| HEAD | Retrieve headers only | allow_method HEAD; |
Used for checking resource existence |
| OPTIONS | Discover available methods | allow_method OPTIONS; |
Can expose server capabilities |
Example: Restricting HTTP Methods
server {
listen 80;
server_name example.com;
location / {
# Allow only GET and POST methods
limit_except GET POST {
deny all;
}
# Additional security headers
add_header X-Content-Type-Options "nosniff";
add_header X-Frame-Options "DENY";
}
}
Note: The
limit_except directive allows specified methods and denies others. This is useful for restricting access to sensitive endpoints.
3. Preventing Clickjacking
| Directive | Description | Values | Notes |
|---|---|---|---|
| add_header | Set HTTP headers to prevent clickjacking | X-Frame-Options: DENY |
Prevents the page from being embedded in a frame or iframe |
| add_header | Set HTTP headers to prevent clickjacking | X-Frame-Options: SAMEORIGIN |
Allows embedding only from the same origin |
| add_header | Set HTTP headers to prevent clickjacking | X-Frame-Options: ALLOW-FROM uri |
Allows embedding from a specific URI (deprecated in favor of CSP) |
| add_header | Set HTTP headers to prevent clickjacking | Content-Security-Policy: frame-ancestors 'none' 'self' http://example.com |
Modern and flexible approach to control frame embedding |
Example: X-Frame-Options DENY
server {
listen 80;
server_name example.com;
add_header X-Frame-Options DENY;
}
Example: Content-Security-Policy frame-ancestors
server {
listen 80;
server_name example.com;
add_header Content-Security-Policy "frame-ancestors 'none';";
}
Note: The
X-Frame-Options header is supported by most modern browsers, but the Content-Security-Policy with frame-ancestors directive is the preferred method as it offers more granular control and better security.
Warning: Setting
X-Frame-Options: ALLOW-FROM is deprecated and should not be used in new implementations.
4. Enabling XSS Protection
| Directive | Description | Values | Notes |
|---|---|---|---|
| add_header | Set security headers | X-Content-Type-Options: nosniff |
Prevents MIME type sniffing |
| add_header | Set security headers | X-Frame-Options: DENY |
Prevents clickjacking attacks |
| add_header | Set security headers | X-XSS-Protection: 1; mode=block |
Enables XSS filtering in browsers |
Example: Configuring XSS Protection Headers
server {
listen 80;
server_name example.com;
add_header X-Content-Type-Options "nosniff";
add_header X-Frame-Options "DENY";
add_header X-XSS-Protection "1; mode=block";
location / {
root /var/www/html;
index index.html;
}
}
Note: The
X-XSS-Protection header is deprecated in modern browsers but still supported for backward compatibility.
5. Setting Content Type Options
| Directive | Values | Description |
|---|---|---|
| add_header | X-Content-Type-Options "nosniff" |
Prevents browsers from MIME-sniffing the content type |
Example: Configuring X-Content-Type-Options
server {
listen 80;
server_name example.com;
add_header X-Content-Type-Options "nosniff" always;
}
Note: The
nosniff value ensures that browsers will not interpret files as a different MIME type than what is declared in the Content-Type header.
6. Configuring SSL Security
| Directive | Description | Example |
|---|---|---|
| ssl_protocols | Enables or disables specific SSL/TLS protocols | ssl_protocols TLSv1.2 TLSv1.3; |
| ssl_ciphers | Specifies the list of ciphers to use for SSL connections | ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512; |
| ssl_prefer_server_ciphers | Enables or disables preference for server-side cipher preferences | ssl_prefer_server_ciphers on; |
| ssl_session_cache | Configures the SSL session cache | ssl_session_cache shared:SSL:10m; |
| ssl_session_timeout | Sets the time during which a client can reuse an SSL session | ssl_session_timeout 10m; |
| ssl_stapling | Enables OCSP stapling for improved performance and security | ssl_stapling on; |
| ssl_stapling_verify | Enables verification of OCSP responses | ssl_stapling_verify on; |
Example: Basic SSL Configuration
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /path/to/certificate.crt;
ssl_certificate_key /path/to/private.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
location / {
root /var/www/html;
index index.html;
}
}
Note: Ensure that the SSL certificate and key files are properly secured with appropriate file permissions.
Warning: Using outdated or weak SSL/TLS protocols (e.g., SSLv2, SSLv3) can expose your server to security vulnerabilities.
7. Implementing HSTS
| Directive | Description | Values | Notes |
|---|---|---|---|
| add_header | HTTP header to add | Strict-Transport-Security |
Enforces HTTPS connections |
| max-age | Duration in seconds | 31536000 |
One year (default) |
| includeSubDomains | Apply to subdomains | true |
Optional, but recommended |
| preload | Preload in browser lists | true |
Optional, for major sites |
Example: Basic HSTS Configuration
server {
listen 443 ssl;
server_name example.com;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}
Example: HSTS with Preload
server {
listen 443 ssl;
server_name example.com;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
}
Note: HSTS headers are only effective over HTTPS. Ensure all traffic is redirected to HTTPS.
Warning: Misconfiguring HSTS can lead to permanent browser redirects. Test thoroughly before deploying.
8. Restricting File Access
| Directive | Description | Default | Notes |
|---|---|---|---|
| location | Defines how to handle requests to a specific URI or path | None | Used to restrict access to sensitive files or directories |
| allow | Allows access from specific IP addresses or networks | None | Can be used in conjunction with deny |
| deny | Denies access from specific IP addresses or networks | None | Can be used in conjunction with allow |
| limit_req | Controls the rate of requests to a location | None | Can be used to prevent brute-force attacks or DDoS |
Example: Restricting Access to Configuration Files
location ~ /config/.*\.(conf|yaml|yml)$ {
deny all;
}
Example: Rate Limiting Requests
limit_req_zone $binary_remote_addr zone=api:10m rate=1r/s;
location /api/ {
limit_req zone=api burst=5 nodelay;
}
Note: The
location directive can be used with regex patterns to match specific file extensions or paths. The deny all directive blocks access to any matching files or directories.
Warning: Misconfiguring access restrictions can lead to unintended exposure of sensitive files or directories. Always test configurations in a staging environment before deploying to production.
9. Securing Admin Paths
| Path | Security Measure | Description |
|---|---|---|
| /admin | auth_basic |
Enables basic HTTP authentication for admin access |
| /admin | allow/deny |
Restricts access to specific IP addresses or ranges |
| /admin | location with limit_req |
Limits the number of requests to admin endpoints |
Example: Basic Auth for Admin
location /admin {
auth_basic "Admin Area";
auth_basic_user_file /etc/nginx/htpasswd;
}
Example: Rate Limiting for Admin
limit_req_zone $binary_remote_addr zone=admin:10m rate=1r/s;
location /admin {
limit_req zone=admin burst=5 nodelay;
}
10. Preventing Buffer Overflow
| Directive | Description | Default | Notes |
|---|---|---|---|
| client_max_body_size | Sets the maximum allowed size of the client request body. | 1m | Prevents large uploads from consuming memory or causing overflow. |
| client_body_buffer_size | Defines the buffer size for reading client request body. | 128k | Controls how much data is read into memory at once. |
| large_client_header_buffers | Defines the number and size of buffers for reading large client request headers. | 4 8k | Increases buffer size to prevent overflow in large headers. |
Example: Configuring Buffer Limits
server {
listen 80;
client_max_body_size 10m;
client_body_buffer_size 256k;
large_client_header_buffers 4 16k;
location /upload {
client_max_body_size 50m;
}
}
Note: Setting
client_max_body_size to a reasonable limit prevents attackers from uploading excessively large files, which can lead to buffer overflows or denial of service.
Warning: If
large_client_header_buffers is too small, Nginx may fail to parse large headers and return a 400 Bad Request error.
11. Using Secure Headers
| Header | Description | Example | Notes |
|---|---|---|---|
| Content-Security-Policy | Prevents cross-site scripting (XSS) and other code injection attacks | Content-Security-Policy: default-src 'self'; script-src 'self' https://apis.google.com; |
Use report-uri to report violations |
| X-Frame-Options | Prevents clickjacking by controlling whether the page can be embedded in an iframe | X-Frame-Options: DENY |
Values: DENY, SAMEORIGIN, ALLOW-FROM uri |
| X-Content-Type-Options | Prevents browsers from interpreting files as a different MIME type than declared | X-Content-Type-Options: nosniff |
Helps prevent MIME type sniffing attacks |
| X-XSS-Protection | Enables XSS filtering in browsers (deprecated but still supported) | X-XSS-Protection: 1; mode=block |
Deprecated in favor of CSP |
| Strict-Transport-Security | Enforces secure (HTTPS) connections to the server | Strict-Transport-Security: max-age=31536000; includeSubDomains |
Use preload for preloading in browsers |
| Referrer-Policy | Controls how much referrer information is sent with requests | Referrer-Policy: no-referrer-when-downgrade |
Values: no-referrer, origin, strict-origin |
Example: Setting Secure Headers in Nginx
server {
listen 443 ssl;
server_name example.com;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://apis.google.com;";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
add_header Referrer-Policy "no-referrer-when-downgrade";
location / {
root /var/www/html;
index index.html;
}
}
Note: CSP is the most powerful security header and should be configured carefully to avoid breaking legitimate functionality.
12. Implementing DDoS Protection
Example: Basic DDoS Protection Configuration
# Limit requests per IP
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
# Apply to specific location
location /api/ {
limit_req zone=api burst=20 nodelay;
}
| Directive | Description | Values |
|---|---|---|
| limit_req_zone | Defines a shared memory zone for rate limiting | $binary_remote_addr zone=name:size rate=r/s |
| limit_req | Applies rate limiting to a location | zone=name [burst=N] [nodelay] |
| burst | Maximum number of requests allowed to exceed the rate limit | Integer |
| nodelay | Whether to delay requests or reject immediately | true | false |
Note: The
limit_req_zone directive uses binary IP addresses for more efficient storage.
Example: Rate Limiting with Different Zones
# Rate limit for login attempts
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
# Rate limit for API calls
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
location /login {
limit_req zone=login burst=5 nodelay;
}
location /api {
limit_req zone=api burst=20 nodelay;
}