Skip to main content

Harden NGINX

This is a guide on how to harden your NGINX server. We'll take a look at a few ways that can help you prevent people with malicious intent to meddle with your web server.

Configuring SSL/TLS

By default, NGINX uses old SSL/TLS protocol versions. These leave you vulnerable to attacks such as BEAST. Disabling older SSL/TLS versions comes at the cost of reducing client compatibility. You can set the protocols within nginx.conf or on a per server block basis.

ssl_protocols TLSv1.2 TLSv1.3;

Configuring Ciphers

Cipher suites are used to negotiate a secure connection. Here we will eliminate ciphers that aren't really used or may be weak. The method used for determining what ciphers to use was to issue the command nmap --script ssl-enum-ciphers -p 443  They use the strongest ciphers available and people don't usually have any issues connecting to their website, so that's what we will support as well.

The order of the following ciphers are strongest to weakest, you can set this within nginx.conf or on a per server block basis:

# Set the server to prefer these ciphers in order
ssl_prefer_server_ciphers on;

Disabling NGINX Server Tokens

By default NGINX displays its current version on error pages and in the "Server" response header field. Disabling this will prevent attackers from knowing your current NGINX version and trying to use exploits if there happens to be one on the version you're running.

server_tokens is enabled by default which can be changed by uncommenting it in /etc/nginx/nginx.conf:

server_tokens off;

HTTP Security Headers

HTTP headers pass additional information between the client and the server. This helps prevent Cross-Site Scripting, Clickjacking and much more. The following headers can be set in nginx.conf or set on a per server block basis. To verify the below headers have been added, enter the command curl -I https://<yourwebsite> which outputs the headers of your site.


HTTP Strict Transport Security let's the browser know that the website should only be accessed using HTTPS. It sets a time period (the recommendation is usually a year) for how long the client should connection via HTTPS only.

The following will set HSTS for one year and will include the same header for all of your subdomains:

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";

Browsers may send referrer information such as what site you came from, it's path and query parameters. If you come from a sensitive page (e.g. admin page of a web service with token in URL) and it has links to other websites, setting the policy will help prevent reporting the whole URL

The header outlined below will send referrers only when in the same origin and within the same protocol level (HTTPS -> HTTPS):

add_header Referrer-Policy "strict-origin";

This header protects against Click-Jacking Attacks. This prevents your content from being embedded from other websites.

Here we only allow embeds that are within the same origin:

add_header X-Frame-Options "SAMEORIGIN";

Your browser may try to do it's own thinking and guess media types being displayed. Without this header your browser may try to execute content that isn't supposed to be executable. This prevents malicious code for running if you allow public uploads or comments on your site.

Setting this header is done by adding:

add_header X-Content-Type-Options "nosniff";

Content Security Policy helps detect and mitigate certain types of attacks such as Cross-Site Scripting and data injection attack. It is a more modern and powerful header than X-XSS-Protection. It allows you to restrict how resources such as JavaScript, CSS and anything else the browser loads.

CSP is very complex and there are a wide variety of options to configure. Look at to see options you can configure and examples.

The following policy allows images, scripts, AJAX, form actions, and CSS from the same origin, and does not allow any other resources to load (eg object, frame, media, etc). It is a good starting point for many sites. Make sure to check your browser console logs to see what content doesn't get loaded and adjust the values accordingly.

add_header Content-Security-Policy "default-src 'none'; script-src 'self'; connect-src 'self'; img-src 'self'; style-src 'self';base-uri 'self';form-action 'self'";

Buffer Size Limitations

Settings a small buffer size will prevent buffer overflow attacks on your server. If a buffer overflow attacks happens to take place, it may allow the execution of malicious code, crashes or even data corruption. These can be configured server wide or on a per server block basis.

client_body_buffer_size handles the buffer size for POST data sent by the client. This usually consists of form submissions, file uploads etc. Setting this to a low value such as 1K bytes is usually sufficient.

client_body_buffer_size 1k;

large_client_header_buffers deals with client headers that go over the standard client_header_buffer_size (default is 1K). 1K will handle most requests but just in case it does go over, we need to tweak the large header values. The defaults are 4 * 8K buffers. Adjust accordingly if you deal with large headers a lot, if it exceeds the buffer then a 414 or 400 error will be returned to the client.

large_client_header_buffers 2 1k;

client_max_body_size sets the maximum allowed size of the client request body. This is what restricts the filesize for uploads via the POST method. If you don't allow uploads then set a low value such as 1K here, otherwise, define a maximum filesize limit.

client_max_body_size 1k;

Disable HTTP Methods

Disabling unused HTTP methods can eliminate a possible attack vector. In most cases, GET, HEAD and POST methods are all you need. Include the following in your server block:

if ($request_method !~ ^(GET|HEAD|POST)$) {
return 444; # Closes the connection, usually throws off bad bots


NGINX is modular in design, so if you don't use a certain feature, you can compile from source and pass through flags to disable a module. Removing modules you don't use helps conserve memory and lessens your attack vector.

You can find out what parameters were built with your NGINX binary by issuing nginx -V in the terminal. You can check out NGINX's guide on installing from source for parameters to disable modules or include additional security modules.