Skip to content

Instantly share code, notes, and snippets.

@gavinhungry
Last active October 20, 2025 00:46
Show Gist options
  • Select an option

  • Save gavinhungry/7a67174c18085f4a23eb to your computer and use it in GitHub Desktop.

Select an option

Save gavinhungry/7a67174c18085f4a23eb to your computer and use it in GitHub Desktop.
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 not included here.
#
# Additional tips:
#
# * Enable CAA DNS record: https://sslmate.com/caa
#
# Example: https://www.ssllabs.com/ssltest/analyze.html?d=gavinhungry.com
#
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 ssl http2 default_server;
listen 443 ssl http2 default_server;
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 -out /etc/ssl/dhparam.pem 4096
# ssl_dhparam /etc/ssl/dhparam.pem;
ssl_protocols TLSv1.3 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ecdh_curve secp521r1:secp384r1;
ssl_ciphers EECDH+AESGCM:EECDH+AES256;
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;
}
@sinns804
Copy link
Copy Markdown

sinns804 commented Sep 8, 2016

Sweet example! Thanks a bunch :D!

@danday74
Copy link
Copy Markdown

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
Copy link
Copy Markdown

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
Copy link
Copy Markdown

jult 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
Copy link
Copy Markdown

perguth 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
Copy link
Copy Markdown

worked for me without OSCP stalping,
Thanks a lot.

@geronime
Copy link
Copy Markdown

geronime 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+.

@C0nw0nk
Copy link
Copy Markdown

C0nw0nk 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

@ItsIrv
Copy link
Copy Markdown

ItsIrv commented Sep 17, 2020

ssl_protocols TLSv1.3 TLSv1.2 TLSv1.1 TLSv1

needs to be changed to

ssl_protocols TLSv1.3 TLSv1.2;

per https://blog.qualys.com/product-tech/2018/11/19/grade-change-for-tls-1-0-and-tls-1-1-protocols

@dbrossard
Copy link
Copy Markdown

Doesn't seem to work. I still get warnings on all CBC ciphers so I only get an A, not A+:

`

TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (0xc028)   ECDH x25519 (eq. 3072 bits RSA)   FS   WEAK 256
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (0xc014)   ECDH x25519 (eq. 3072 bits RSA)   FS   WEAK 256
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 (0xc027)   ECDH x25519 (eq. 3072 bits RSA)   FS   WEAK 128
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (0xc013)   ECDH x25519 (eq. 3072 bits RSA)   FS   WEAK 128
TLS_RSA_WITH_AES_128_CBC_SHA (0x2f)   WEAK 128
`

@dlangille
Copy link
Copy Markdown

Doesn't seem to work. I still get warnings on all CBC ciphers so I only get an A, not A+:

Note the last update to this gist was on Nov 2, 2018

@gavinhungry
Copy link
Copy Markdown
Author

@ItsIrv @dbrossard @dlangille,

Thanks for the reminders that I've been letting this stagnate. I've just now applied what I think are sensible updates - feedback/tips/corrections welcome.

@dlangille
Copy link
Copy Markdown

@ItsIrv @dbrossard @dlangille,

Thanks for the reminders that I've been letting this stagnate. I've just now applied what I think are sensible updates - feedback/tips/corrections welcome.

Take anything you like from what I just put up: https://github.com/FreshPorts/nginx-config/blob/main/virtualhost-common-ssl.conf

@cwbeck
Copy link
Copy Markdown

cwbeck commented Jun 21, 2021

@gavinhungry

Thanks for posting this. Have you found any issues with nginx performance when terminating HTTPS traffic? I've found it to be 10x-15x slower than HTTP. I understand there is extra processing involved but benchmarks I'm getting don't look great at all. I'm wondering if there is something I'm missing here.

@polynomialspace
Copy link
Copy Markdown

ssl_ciphers EECDH+AESGCM:EECDH+AES256;

You can probably remove the dhparam bits, afaik dhparam isn't used for EECDH.

@beatquantum
Copy link
Copy Markdown

Hi Gavin, Hi all,

This works for me with nginx 1.21.3 and openssl 1.1.1l - it is in the server block.

server {
...
# Some cybersecurity tools use TLSv1.2 otherwise I would have used only TLSv1.3
ssl_protocols TLSv1.3 TLSv1.2;

    # The TLS 1.2 ciphers below will not work with very old browsers and Android phones.
    # Please do not ignore the +AES256 as otherwise you will get AES128.
    ssl_ciphers ECDH+CHACHA20:ECDH+AESGCM+AES256;

    # The TLS 1.3 ciphers below are fewer than those in the RFC. But they work.
    ssl_conf_command Ciphersuites TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384;

    # The commands below directly affect how Openssl will behave.
    ssl_conf_command Options ServerPreference,PrioritizeChaCha,NoRenegotiation,NoResumptionOnRenegotiation;

    # My server is not heavy loaded so I am okay to use secp384r1. Saw no effect on website speed.
    ssl_ecdh_curve secp521r1:secp384r1;

    # Do not use 2048 (see SSLTEST rating guide)
    ssl_dhparam /etc/ssl/dsa4096.pem;

...
}

@teggsdgreat
Copy link
Copy Markdown

Beautiful, it worked

@ray-moncada
Copy link
Copy Markdown

Do you need to add the header "Content-Security-Policy" for extra security measures?

@stevenlafl
Copy link
Copy Markdown

stevenlafl commented Jun 10, 2022

Here is one from the NSA/defense.gov, ref https://media.defense.gov/2021/Jan/05/2002560140/-1/-1/0/ELIMINATING_OBSOLETE_TLS_UOO197443-20.PDF & https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-52r2.pdf

ssl_protocols TLSv1.3 TLSv1.2;
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:AES256-GCM-SHA384;
ssl_ecdh_curve secp384r1;	
ssl_prefer_server_ciphers on;
  
ssl_session_tickets off;

https://github.com/nsacyber/Mitigating-Obsolete-TLS/blob/master/webserver/nginx-tls.txt

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment