Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Nginx SSL/TLS configuration for "A+" Qualys SSL Labs rating
#
# Name: nginx-tls.conf
# Auth: Gavin Lloyd <gavinhungry@gmail.com>
# Desc: Nginx SSL/TLS configuration for "A+" Qualys SSL Labs rating
#
# Enables HTTP/2, PFS, HSTS and OCSP stapling. Configuration options not related
# to SSL/TLS are omitted here.
#
# Example: https://www.ssllabs.com/ssltest/analyze.html?d=gavinhungry.io
#
server {
listen [::]:80;
listen 80;
server_name domain.tld www.domain.tld;
# Redirect all non-https requests
rewrite ^ https://$host$request_uri? permanent;
}
server {
listen [::]:443 default_server ssl http2;
listen 443 default_server ssl http2;
server_name domain.tld www.domain.tld;
# Certificate(s) and private key
ssl_certificate /etc/ssl/domain.crt;
ssl_certificate_key /etc/ssl/domain.key;
# RFC-7919 recommended: https://wiki.mozilla.org/Security/Server_Side_TLS#ffdhe4096
ssl_dhparam /etc/ssl/ffdhe4096.pem;
# Or, generate random dhparam
# openssl dhparam 4096 -out /etc/ssl/dhparam.pem
# ssl_dhparam /etc/ssl/dhparam.pem;
ssl_protocols TLSv1.3 TLSv1.2 TLSv1.1 TLSv1;
ssl_prefer_server_ciphers on;
ssl_ciphers EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA512:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:ECDH+AESGCM:ECDH+AES256:DH+AESGCM:DH+AES256:RSA+AESGCM:!aNULL:!eNULL:!LOW:!RC4:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS;
ssl_session_cache shared:TLS:2m;
ssl_buffer_size 4k;
# OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 1.0.0.1 [2606:4700:4700::1111] [2606:4700:4700::1001]; # Cloudflare
# Set HSTS to 365 days
add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains; preload' always;
}
@yuanyuanlife

This comment has been minimized.

Copy link

commented Mar 23, 2016

Haven't you generate a stronger DHE parameter?

@gavinhungry

This comment has been minimized.

Copy link
Owner Author

commented Sep 7, 2016

@bokeyy,

Thanks, I've updated the Gist.

@sinns804

This comment has been minimized.

Copy link

commented Sep 8, 2016

Sweet example! Thanks a bunch :D!

@joe1981alAPR

This comment has been minimized.

Copy link

commented Nov 3, 2016

Works great with few modifications...

The spdy option has been superseded with http2

Also I would recommend expanding your resolver line: resolver 8.8.8.8 8.8.4.4 [2001:4860:4860::8888] [2001:4860:4860::8844]

@danday74

This comment has been minimized.

Copy link

commented Jan 30, 2017

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; ... if you want A+ on a subdomain ... also, if using Let's encrypt and you want a score of 100 on key exchange then you must generate them with the flag --rsa-key-size 4096 and the usual openssl dhparam -out dhparam.pem 4096

@danday74

This comment has been minimized.

Copy link

commented Jan 30, 2017

another missing entry ... ssl_ecdh_curve secp384r1; ... required in addition to above for score of 100 on key exchange

@danday74

This comment has been minimized.

Copy link

commented Jan 31, 2017

Based on this config, 5 others and some extensive testing, I have put together a config that describes exactly how each NGINX directive will effect your SSL Labs score - see http://stackoverflow.com/questions/41930060/how-do-you-score-a-with-100-on-all-categories-on-ssl-labs-test-with-lets-encry - thanks for helping me put it together

@jult

This comment has been minimized.

Copy link

commented Nov 16, 2017

You can use dnsmasq for even faster and more secure lookups for the resolver entry, and then use my include= example
for TLS/SSL with nginx: https://gist.github.com/jult/395ad9fd3e9773a54a67aaf689beab27

@perguth

This comment has been minimized.

Copy link

commented Jan 15, 2018

I'd rather go for the 2048 long ssl_dhparam on my RaspberryPi like recommended here: https://gist.github.com/plentz/6737338#file-nginx-conf-L57 It just takes too damn long. 😂

@ldhgrp-digtial

This comment has been minimized.

Copy link

commented Jan 27, 2018

worked for me without OSCP stalping,
Thanks a lot.

@geronime

This comment has been minimized.

Copy link

commented Mar 5, 2018

Based on https://security.stackexchange.com/a/95184/172143 it might make sense to use the -dsaparam option for the openssl dhparam command. Instead of hours it takes just a while to generate and I still get an A+.

@jult

This comment has been minimized.

Copy link

commented Mar 14, 2018

@geronime Yes, I had that advice posted in a comment at https://gist.github.com/jult/395ad9fd3e9773a54a67aaf689beab27 already.

@md-mg

This comment has been minimized.

Copy link

commented Mar 21, 2018

SSLLabs recommends to remove RSA+AESGCM from the cipher-list (weak)

@muratak18

This comment has been minimized.

Copy link

commented Mar 30, 2018

FYI: This directive appeared in version 1.3.7
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8; # google-public-dns-a.google.com

@C0nw0nk

This comment has been minimized.

Copy link

commented Sep 24, 2018

Here is my code for their resolver to support both IPv4 and IPv6 (Can disable IPv6 and will still work universal setup)

#Cloudflare resolver 1dot1dot1dot1.cloudflare-dns.com
resolver 1.1.1.1 1.0.0.1 [2606:4700:4700::1111] [2606:4700:4700::1001];

https://1.1.1.1/#explanation
https://www.cloudflare.com/learning/dns/what-is-1.1.1.1/
https://blog.cloudflare.com/announcing-1111/

Fastest DNS resolvers in the world (1.1.1.1 is the fastest most secured and private unlike google)
https://www.dnsperf.com/#!dns-resolvers

@steakknife

This comment has been minimized.

Copy link

commented Sep 25, 2018

Don't generate a dhparam; this is cargo-cult nonsense. Use the vetted ffdhe4096

-----BEGIN DH PARAMETERS-----
MIICCAKCAgEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz
+8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a
87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7
YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi
7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD
ssbzSibBsu/6iGtCOGEfz9zeNVs7ZRkDW7w09N75nAI4YbRvydbmyQd62R0mkff3
7lmMsPrBhtkcrv4TCYUTknC0EwyTvEN5RPT9RFLi103TZPLiHnH1S/9croKrnJ32
nuhtK8UiNjoNq8Uhl5sN6todv5pC1cRITgq80Gv6U93vPBsg7j/VnXwl5B0rZp4e
8W5vUsMWTfT7eTDp5OWIV7asfV9C1p9tGHdjzx1VA0AEh/VbpX4xzHpxNciG77Qx
iu1qHgEtnmgyqQdgCpGBMMRtx3j5ca0AOAkpmaMzy4t6Gh25PXFAADwqTs6p+Y0K
zAqCkc3OyX3Pjsm1Wn+IpGtNtahR9EGC4caKAH5eZV9q//////////8CAQI=
-----END DH PARAMETERS-----

And, reduce ssl_buffer_size to get latency down.

ssl_buffer_size 4k;

Source

Also, resolver lines should look like this:

resolver 1.1.1.1 1.0.0.1 [2606:4700:4700::1111] [2606:4700:4700::1001] valid=300s; # cloudflare DNS per @C0nw0nk
resolver valid=300s; # fall-back to system resolvers should cloudflare be unreachable 
resolver_timeout 5s; # fail faster
# system resolvers should be something like powerdns recursor proxying 6-10 instances of dnscrypt with different servers
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.