Nginx reverse proxy security and performance best practices

Max Shahdoost
5 min readJul 28, 2023

--

About a month ago I published an article about a practical way to implement nginx reverse proxy but I’ve got feedback from some dudes that the article is suffering heavily from a security perspective so I decided to publish this article and cover all the major security best practices so that you can make sure everything will be fine.

Link to the main article:
https://maxtsh.medium.com/a-practical-guide-to-implementing-reverse-proxy-using-dockerized-nginx-with-multiple-apps-ad80f6dfce17

1- Basic Security

Minimizing the amount of data that is revealed to potential attackers is a key way to safeguard your web server. This can include details such as the version number of Nginx, PHP, and the operating system. This type of information is often included in HTTP headers, but it can be hidden to better protect your server. To achieve this:

server_tokens off;

Using TLS 1.2 and TLS 1.3 on an nginx server is important because these versions of the TLS protocol provide stronger security features and improved performance compared to older versions. Some of the key benefits of using TLS 1.2 and TLS 1.3 include:

  • Stronger encryption: TLS 1.2 and TLS 1.3 use stronger encryption algorithms and key lengths to protect data transmitted over the internet.
  • Improved performance: TLS 1.2 and TLS 1.3 are designed to be more efficient than older versions of the TLS protocol, which can result in faster connection times and improved overall performance.
  • Improved security: TLS 1.2 and TLS 1.3 include additional security features, such as Perfect Forward Secrecy, which helps to prevent an attacker from using previously recorded traffic to decrypt current traffic.

Overall, using TLS 1.2 and TLS 1.3 helps to protect the confidentiality, integrity, and availability of data transmitted over the internet, and it is an important security measure for any server that handles sensitive information.

ssl_protocols TLSv1.2 TLSv1.3;

2- Cipher Suits

There are four main types of encryption algorithms:

  • Key exchange
  • Authentication
  • Block encryption
  • Message Authentication

Some of these algorithms, such as RC4, DH, 3DES, and EXP, should be avoided due to their lower levels of security. It is important to prioritize the use of more secure algorithms in order to balance security with accessibility for customers.

For more information on the security of different cipher suites, you can refer to the following link: https://ciphersuite.info/

ssl_ciphers ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-CCM:DHE-RSA-AES256-CCM8:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-CCM:DHE-RSA-AES128-CCM8:DHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256;

3- Optimization and Gzip

The “ssl_prefer_server_ciphers” directive tells the server to use its own list of preferred ciphers, rather than relying on the client to specify them.

# Perfect Forward Secrecy(PFS) is frequently compromised without this
ssl_prefer_server_ciphers on;

The “ssl_session_tickets” directive is used to disable the use of SSL session tickets, which are used to resume SSL sessions and improve performance.

ssl_session_tickets off;

The “ssl_session_timeout” and “ssl_session_cache” directives enable SSL session caching, which helps to improve performance by allowing the server to reuse previously established SSL sessions.

# Enable SSL session caching for improved performance
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;

The “ssl_buffer_size” directive sets the size of the buffer used for sending data over SSL.

# By default, the buffer size is 16k, which corresponds to minimal overhead when sending big responses.
# To minimize Time To First Byte it may be beneficial to use smaller values
ssl_buffer_size 8k;

The “ssl_stapling” and “ssl_stapling_verify” directives enable OCSP (Online Certificate Status Protocol) stapling, which is a method of checking the revocation status of SSL certificates without the need for a separate request to the certificate authority. This helps to improve performance by reducing the number of requests that need to be made.

# OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;

Gzip configs:

gzip on;
gzip_disable "msie6";
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_min_length 256;
gzip_types
application/atom+xml
application/geo+json
application/javascript
application/x-javascript
application/json
application/ld+json
application/manifest+json
application/rdf+xml
application/rss+xml
application/xhtml+xml
application/xml
font/eot
font/otf
font/ttf
image/svg+xml
text/css
text/javascript
text/plain
text/xml;

4- Security Headers

The X-Content-Type-Options response HTTP header is a marker used by the server to indicate that the MIME types advertised in the Content-Type headers should be followed and not be changed. The header allows you to avoid MIME type sniffing by saying that the MIME types are deliberately configured.

Content Security Policy (CSP) is an added layer of security that helps to detect and mitigate certain types of attacks, including Cross-Site Scripting (XSS) and data injection attacks. These attacks are used for everything from data theft, to site defacement, to malware distribution. This CSP configuration is highly secure, but it is recommended to test it first to ensure that it does not block third party scripts. CSP Evaluator is an excellent tool for testing your CSP configuration.

The HTTP Strict-Transport-Security response header (often abbreviated as HSTS) informs browsers that the site should only be accessed using HTTPS, and that any future attempts to access it using HTTP should automatically be converted to HTTPS.

# Security headers
## X-Content-Type-Options: avoid MIME type sniffing
add_header X-Content-Type-Options nosniff;

## Content-Security-Policy (CSP): Yes
## No 'script-src' directive, you need to test it yourself
add_header Content-Security-Policy "object-src 'none'; base-uri 'none'; require-trusted-types-for 'script'; frame-ancestors 'self';";
## The safest CSP, only block your website to be inside an inframe
add_header Content-Security-Policy "frame-ancestors 'self';";

## Strict Transport Security (HSTS): Yes
add_header Strict-Transport-Security "max-age=15552001; includeSubdomains; preload";

5- Location Block Security Headers

Adding the below x-forwarded-for header is required for security practices.

location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 900;
}

Complete Config File:

server {
listen 443 ssl http2;
listen [::]:443 ssl http2;

server_name __REDACTED__;
ssl_certificate __REDACTED__ ;
ssl_certificate_key __REDACTED__;

# Only return Nginx in server header
server_tokens off;

ssl_dhparam /etc/ssl/certs/dhparam.pem;

ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-CCM:DHE-RSA-AES256-CCM8:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-CCM:DHE-RSA-AES128-CCM8:DHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256;

# Perfect Forward Secrecy(PFS) is frequently compromised without this
ssl_prefer_server_ciphers on;

ssl_session_tickets off;
# Enable SSL session caching for improved performance
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
# By default, the buffer size is 16k, which corresponds to minimal overhead when sending big responses.
# To minimize Time To First Byte it may be beneficial to use smaller values
ssl_buffer_size 8k;

# OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;

# Security headers
## X-Content-Type-Options: avoid MIME type sniffing
add_header X-Content-Type-Options nosniff;
## Content-Security-Policy (CSP): Yes
## No 'script-src' directive, you need to test it yourself
add_header Content-Security-Policy "object-src 'none'; base-uri 'none'; require-trusted-types-for 'script'; frame-ancestors 'self';";
## The safest CSP, only block your website to be inside an inframe
# add_header Content-Security-Policy "frame-ancestors 'self';";
## Strict Transport Security (HSTS): Yes
add_header Strict-Transport-Security "max-age=15552001; includeSubdomains; preload";

}

Resource:
https://beguier.eu/nicolas/articles/nginx-tls-security-configuration.html

--

--

Max Shahdoost

A highly passionate and motivated Frontend Engineer with a good taste for re-usability, security, and developer experience.