[Nginx] Nginx HTTPS Security and Performance Optimization

Enable HTTPS

1
2
3
4
server {
listen 443 ssl;
}
error_page 497 https://$host$request_uri;

Security

Emitting nginx version

Emitting or disable nginx version.

1
server_tokens off;
1
2
3
4
Syntax:	server_tokens on | off | build | string;
Default:
server_tokens on;
Context: http, server, location

Enables or disables emitting nginx version on error pages and in the “Server” response header field.

The build parameter (1.11.10) enables emitting a build name along with nginx version.

Additionally, as part of our commercial subscription, starting from version 1.9.13 the signature on error pages and the “Server” response header field value can be set explicitly using the string with variables. An empty string disables the emission of the “Server” field.

server_tokens | Module ngx_http_core_module - http://nginx.org/en/docs/http/ngx_http_core_module.html#server_tokens

HTTPS

To configure an HTTPS server, the ssl parameter must be enabled on listening sockets in the server block, and the locations of the server certificate and private key files should be specified:

Example:

1
2
3
4
5
6
7
8
9
server {
listen 443 ssl;
server_name www.example.com;
ssl_certificate www.example.com.crt;
ssl_certificate_key www.example.com.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
...
}

Configuring HTTPS servers - http://nginx.org/en/docs/http/configuring_https_servers.html

Enable HTTPS

1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# certs sent to the client in SERVER HELLO are concatenated in ssl_certificate
ssl_certificate /path/to/signed_cert_plus_intermediates;
ssl_certificate_key /path/to/private_key;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;

ssl_session_ticket_key /etc/nginx/conf.d/tls_session_ticket.key;
ssl_session_tickets on;

keepalive_timeout 75s;

# Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
ssl_dhparam /path/to/dhparam.pem;

# intermediate configuration. tweak to your needs.
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
ssl_prefer_server_ciphers on;

# HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
add_header Strict-Transport-Security max-age=15768000;

Strict SNI

SNI, or Server Name Indication, is an addition to the TLS encryption protocol that enables a client device to specify the domain name it is trying to reach in the first step of the TLS handshake, preventing common name mismatch errors.

We can confirm this on the command line with:

1
2
3
$ nginx -V
...
TLS SNI support enabled

If you do not have a line like this one, then Nginx will have to be re-compiled manually to include this support.

1
2
3
4
5
6
7
http {
# Enable Strict SNI
strict_sni on; # off

# Enable the check for invalid domain names.
strict_sni_header on; # off
}

Return directive redirect HTTP to HTTPS

1
2
3
4
5
server {
listen 80;
server_name cloudolife.com;
return 301 https://$host$request_uri;
}

Error_page 497 make HTTP Request Sent to HTTPS Port

Status code 497 used when the client has made a HTTP request to a port listening for HTTPS requests.

1
error_page 497 =301 https://$host$request_uri;

Performance optimization

aio

1
2
3
4
5
Syntax:	aio on | off | threads[=pool];
Default:
aio off;
Context: http, server, location
This directive appeared in version 0.8.11.

Enables or disables the use of asynchronous file I/O (AIO) on FreeBSD and Linux:

1
aio on;

Files can be read and sent using multi-threading (1.7.11), without blocking a worker process:

1
aio threads;

By default, multi-threading is disabled, it should be enabled with the --with-threads configuration parameter. Currently, multi-threading is compatible only with the epoll, kqueue, and eventport methods. Multi-threaded sending of files is only supported on Linux.

sendfile

1
2
3
4
Syntax:	sendfile on | off;
Default:
sendfile off;
Context: http, server, location, if in location

Enables or disables the use of sendfile().

1
sendfile on;

sendfile() is called with the SF_NODISKIO flag which causes it not to block on disk I/O, but, instead, report back that the data are not in memory. nginx then initiates an asynchronous data load by reading one byte.

In this configuration, On the first read, the FreeBSD kernel loads the first 128K bytes of a file into memory, although next reads will only load data in 16K chunks. This can be changed using the read_ahead directive.

tcp_nodelay

1
2
3
4
Syntax:	tcp_nodelay on | off;
Default:
tcp_nodelay on;
Context: http, server, location

Enables or disables the use of the TCP_NODELAY option. The option is enabled when a connection is transitioned into the keep-alive state. Additionally, it is enabled on SSL connections, for unbuffered proxying, and for WebSocket proxying.

tcp_nopush

1
2
3
4
Syntax:	tcp_nopush on | off;
Default:
tcp_nopush off;
Context: http, server, location

Enables or disables the use of the TCP_NOPUSH socket option on FreeBSD or the TCP_CORK socket option on Linux. The options are enabled only when sendfile is used. Enabling the option allows

sending the response header and the beginning of a file in one packet, on Linux and FreeBSD 4.*;
sending a file in full packets.

Example

1
2
3
4
5
6
location /video/ {
sendfile on;
tcp_nodelay on;
tcp_nopush on;
aio on;
}

keepalive, keepalive_timeout, keepalive_requests

1
2
3
4
5
6
7
8
9
10
http {
keepalive_timeout 120s 120s;
keepalive_requests 10000;

upstream BACKEND {
server 192.168.0.18080 weight=1 max_fails=2 fail_timeout=30s;
server 192.168.0.28080 weight=1 max_fails=2 fail_timeout=30s;
keepalive 300;
}
}
1
2
3
4
5
6
7
8
http {
server {
location / {
proxy_http_version 1.1; // 这两个最好也设置
proxy_set_header Connection "";
}
}
}

HTTPS server optimization

Accessing Nginx via https is generally 30% slower than http access. Improve the performance of Nginx + HTTPS for better TTFB and less latency.

SSL operations consume extra CPU resources. On multi-processor systems several worker processes should be run, no less than the number of available CPU cores. The most CPU-intensive operation is the SSL handshake. There are two ways to minimize the number of these operations per client: the first is by enabling keepalive connections to send several requests via one connection and the second is to reuse SSL session parameters to avoid SSL handshakes for parallel and subsequent connections.

Enable HTTP/2

Networking protocol for low-latency transport of content over the web. Originally started out from the SPDY protocol, now standardized as HTTP version 2.

HTTP/2 protocol | Can I use… Support tables for HTML5, CSS3, etc - https://caniuse.com/http2

Check Nginx and OpenSSL version

  • Nginx >= 1.9.5
1
2
# nginx -V
nginx version: nginx/1.20.1
1
2
3
server {
listen 443 ssl http2;
}

Enable SSL session cache

Enabling SSL Session caching can reduce repeated TLS verification and reduce TLS handshake. 1M of memory can cache 4000 connections, which is very cost-effective.

1
2
3
4
5
6
server {
# 1m for 4000 connections.
ssl_session_cache shared:SSL:50m;
# 4 hour during which sessions can be re-used.
ssl_session_timeout 4h;
}

Disable SSL session tickets

Since SSL session tickets have not yet been implemented in Nginx, they can be closed.

1
2
3
4
5
Syntax:	ssl_session_tickets on | off;
Default:
ssl_session_tickets on;
Context: http, server
This directive appeared in version 1.5.9.

Enables or disables session resumption through TLS session tickets - https://tools.ietf.org/html/rfc5077.

1
2
3
server {
ssl_session_tickets off;
}

Enable OCSP Stapling

If you do not enable OCSP Stapling, you need to verify the certificate when the user connects to your server. The time for verifying the certificate is uncontrollable. After we enable OCSP Stapling, you can save this step.

1
2
3
4
5
Syntax:	ssl_stapling on | off;
Default:
ssl_stapling off;
Context: http, server
This directive appeared in version 1.3.7.

Enables or disables stapling of OCSP responses - https://tools.ietf.org/html/rfc6066#section-8 by the server.

1
2
3
4
5
Syntax:	ssl_stapling_verify on | off;
Default:
ssl_stapling_verify off;
Context: http, server
This directive appeared in version 1.3.7.

Enables or disables verification of OCSP responses by the server.

For verification to work, the certificate of the server certificate issuer, the root certificate, and all intermediate certificates should be configured as trusted using the ssl_trusted_certificate directive.

1
2
3
4
Syntax:	ssl_trusted_certificate file;
Default: —
Context: http, server
This directive appeared in version 1.3.7.

Specifies a file with trusted CA certificates in the PEM format used to verify client certificates and OCSP responses if ssl_stapling is enabled.

In contrast to the certificate set by ssl_client_certificate, the list of these certificates will not be sent to clients.

1
2
3
4
5
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /path/to/full_chain.pem;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;

Reduce SSL buffer size

ssl_buffer_size controls the size of the buffer when sending data. In order to minimize the TTFB (time to the first byte), it is best to use a smaller value, so that TTFB can save about 30 – 50ms.

1
2
3
4
5
Syntax:	ssl_buffer_size size;
Default:
ssl_buffer_size 16k;
Context: http, server
This directive appeared in version 1.5.9.

Sets the size of the buffer used for sending data.

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, for example:

1
ssl_buffer_size 4k;

SSL protocols and ciphers

ssl_ciphers

Put the newer and faster Cipher in front, so that the delay is smaller.

1
2
3
4
Syntax:	ssl_ciphers ciphers;
Default:
ssl_ciphers HIGH:!aNULL:!MD5;
Context: http, server

Specifies the enabled ciphers. The ciphers are specified in the format understood by the OpenSSL library, for example:

1
2
3
4
5
ssl_ciphers ALL:!aNULL:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;

# ssl_ciphers ALL:!kEDH!ADH:RC4+RSA:+HIGH:+EXP;

# ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';

The full list can be viewed using the openssl ciphers command.

ssl_prefer_server_ciphers

1
2
3
4
Syntax:	ssl_prefer_server_ciphers on | off;
Default:
ssl_prefer_server_ciphers off;
Context: http, server

Specifies that server ciphers should be preferred over client ciphers when using the SSLv3 and TLS protocols.

1
2
# prefer a list of ciphers to prevent old and slow ciphers
ssl_prefer_server_ciphers on;

ssl_protocols

1
2
3
4
Syntax:	ssl_protocols [SSLv2] [SSLv3] [TLSv1] [TLSv1.1] [TLSv1.2] [TLSv1.3];
Default:
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
Context: http, server

Enables the specified protocols.

TLS1.3

Version 1.3 (the latest one) of the Transport Layer Security (TLS) browser support:

TLS 1.3 | Can I use… Support tables for HTML5, CSS3, etc - https://caniuse.com/tls1-3

Check Nginx and OpenSSL version:

  • Nginx >= 1.13.0

  • OpenSSL >= 1.1.1

1
2
3
4
5
# nginx -V
nginx version: nginx/1.20.1
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-44) (GCC)
built with OpenSSL 1.1.1g FIPS 21 Apr 2020
TLS SNI support enabled

Remember to make all server(include default server) to use TLSv1.3 protocol;


Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
server {
# Default
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

# All protocols.
# ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

# Just TLS v1.3 support.
# ssl_protocols TLSv1.3;

# Only TLS v1.2, v1.3 support.
ssl_protocols TLSv1.2 TLSv1.3;

# ssl_ciphers [TLS13+AESGCM+AES128|TLS13+AESGCM+AES256|TLS13+CHACHA20]:[EECDH+ECDSA+AESGCM+AES128|EECDH+ECDSA+CHACHA20]:EECDH+ECDSA+AESGCM+AES256:EECDH+ECDSA+AES128+SHA:EECDH+ECDSA+AES256+SHA:[EECDH+aRSA+AESGCM+AES128|EECDH+aRSA+CHACHA20]:EECDH+aRSA+AESGCM+AES256:EECDH+aRSA+AES128+SHA:EECDH+aRSA+AES256+SHA:RSA+AES128+SHA:RSA+AES256+SHA:RSA+3DES;

# Specifies that server ciphers should be preferred over client ciphers when using the SSLv3 and TLS protocols
ssl_prefer_server_ciphers off;
}

Early Data (0-RTT) (Optional)

1
2
3
4
Syntax:	ssl_early_data on | off;
Default:
ssl_early_data off;
Context: http, server

Enables or disables TLS 1.3 early data.

Requests sent within early data are subject to replay attacks. To protect against such attacks at the application layer, the $ssl_early_data variable should be used.

1
2
3
4
5
server {
ssl_early_data on;
# In addition, please add Early-Data header to inform the backend to prevent replay attacks
proxy_set_header Early-Data $ssl_early_data;
}

References

[1] Configuring HTTPS servers - http://nginx.org/en/docs/http/configuring_https_servers.html

[2] Module ngx_http_ssl_module - http://nginx.org/en/docs/http/ngx_http_ssl_module.html

[3] hakasenyang/openssl-patch: OpenSSL & NginX Patch - https://github.com/hakasenyang/openssl-patch

[4] rfc7540 - https://datatracker.ietf.org/doc/html/rfc7540

[5] X-Frame-Options - HTTP | MDN - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options

[6] Types of attacks - Web security | MDN - https://developer.mozilla.org/en-US/docs/Web/Security/Types_of_attacks

[7] How to configure Security Headers in Nginx and Apache - https://webdock.io/en/docs/how-guides/security-guides/how-to-configure-security-headers-in-nginx-and-apache

[8] Module ngx_http_core_module - http://nginx.org/en/docs/http/ngx_http_core_module.html

[9] Module ngx_http_headers_module - http://nginx.org/en/docs/http/ngx_http_headers_module.html

[10] NGINX Docs | Configuration Guide - https://docs.nginx.com/nginx-app-protect/configuration/

[11] What is SNI? How TLS server name indication works | Cloudflare - https://www.cloudflare.com/learning/ssl/what-is-sni/