Category: Web Server and InfrastructureUpdated 2026-10-18

ssl_protocols

ssl_protocols is the nginx directive that sets which SSL and TLS protocol versions a server will negotiate with clients. In current nginx documentation the default is TLSv1.2 TLSv1.3, and RFC 8996 forbids TLS 1.0 and 1.1. You set it in the http or server context to keep outdated versions disabled while modern browsers still connect.

Syntax, accepted values and defaults

The directive belongs to the ngx_http_ssl_module. It is valid in the http and server contexts:

ssl_protocols [SSLv2] [SSLv3] [TLSv1] [TLSv1.1] [TLSv1.2] [TLSv1.3];

The documented default is ssl_protocols TLSv1.2 TLSv1.3;. The documentation notes that TLSv1.3 has been part of the default since nginx 1.23.4, so a server running an older build, or one with an old configuration, can behave differently. The parameters also depend on the OpenSSL library nginx uses: TLSv1.1 and TLSv1.2 need OpenSSL 1.0.1 or newer, and TLSv1.3 (nginx 1.13.0 and later) needs OpenSSL 1.1.1 or newer. Run nginx -V to see which library your build uses.

The syntax still accepts SSLv2, SSLv3, TLSv1 and TLSv1.1, but there is no reason to enable them. RFC 8996 says that TLS 1.0 MUST NOT be used and TLS 1.1 MUST NOT be used.

A minimal configuration and how to test it

Spelling the versions out makes the policy visible in your configuration instead of depending on a default that changed between releases:

server {
    listen 443 ssl;
    server_name example.com;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_certificate     /etc/ssl/example.com/fullchain.pem;
    ssl_certificate_key /etc/ssl/example.com/privkey.pem;
}

Validate and apply it, then check what is negotiated from another machine:

sudo nginx -t && sudo systemctl reload nginx
openssl s_client -connect example.com:443 -servername example.com -tls1_3 -brief </dev/null
openssl s_client -connect example.com:443 -servername example.com -tls1_2 -brief </dev/null
nmap --script ssl-enum-ciphers -p 443 example.com

The two s_client lines prove that the allowed versions work. To prove that old versions are refused, rely on the nmap script, because a modern OpenSSL client may refuse to speak TLS 1.1 itself and its failure would say nothing about your server. Run nmap only against servers you own. To see whether anyone would be affected before you drop a version, log the $ssl_protocol variable in your access log format.

Gotchas: default server, ciphers and old clients

Where you put the directive matters. The handshake happens before nginx has read the Host header, so the documentation warns that a value set at the server level may be taken from the default server for the same address and port. Set ssl_protocols in the http block, or make sure every server on a shared listen uses the same value. To refuse handshakes for unknown names altogether, nginx 1.19.4 added ssl_reject_handshake on; for a catch-all default server.

ssl_protocols and ssl_ciphers are separate controls. The latter defaults to HIGH:!aNULL:!MD5 and, per the OpenSSL ciphers manual, a cipher list string covers TLS 1.2 and below, while TLS 1.3 suites are set separately (in nginx through ssl_conf_command, available since 1.19.4). Allowing a version does not choose strong ciphers for it.

Where it fits in server hardening

Restricting protocol versions is one line in a larger TLS baseline: a valid certificate chain, sensible ciphers, HTTP security headers, and renewal that actually reloads nginx. The nginx security best practices guide shows the full TLS block in context, and the TLS / SSL page explains why the name "SSL" survives even though the protocol does not.

Operationally, treat the directive like any other security setting. Keep it in version control, test with nginx -t before each reload, and recheck from outside after every nginx or OpenSSL upgrade, since defaults can change between releases. Shorter certificate lifetimes make reload automation more important, and the 45-day certificates article covers renewal hooks. The VPS security checklist lists the neighbouring controls.

A TLS version scan is a snapshot. Schedule it, so that a regression introduced by a configuration change is noticed in days rather than months.

Related terms: TLS / SSL, nginx, Security headers, server_tokens, limit_req.

Frequently asked questions

What is the default value of ssl_protocols in nginx?

The nginx documentation lists ssl_protocols TLSv1.2 TLSv1.3; as the default, and notes that TLSv1.3 has been in the default since version 1.23.4. Setting it explicitly avoids surprises between versions.

Should I disable TLS 1.0 and TLS 1.1 in nginx?

Yes. RFC 8996 states that TLS 1.0 and TLS 1.1 must not be used, so leave them out of ssl_protocols.

Does ssl_protocols enable TLS 1.3?

Only if nginx is built against OpenSSL 1.1.1 or newer and TLSv1.3 appears in the list or in the default. Check the library with nginx -V.

Does ssl_protocols choose the cipher suites?

No. ssl_ciphers handles TLS 1.2 and older cipher lists, while TLS 1.3 suites are configured separately, for example through ssl_conf_command.

Why was my ssl_protocols change ignored on one virtual host?

The handshake happens before nginx knows the requested name, so the value from the default server for that address and port can apply. Set the directive in the http block or keep it identical across servers on the same listen.

Want to see where your own server stands?

Run the free, read-only server check, or open the Security Lab and watch the detect, contain, recover, verify loop in action.

Get your free server checkOpen the Security Lab