HOWTO: A+ with all 100%’s on SSL Labs test using Nginx mainline & stable

This Wiki is last updated on 2/26/2020.

Steps to get you all 100% and A+ using Nginx mainline & stable version

  • Certificate Section

This section is easy to get 100% on.
Make sure your cert and chain are in the correct order.
Don’t use SHA1 (use SHA256) for the signature algorithm.
Use a well known/trusted CA.

  • Protocol Support Section

Since the release of TLSv1.3 and depreciation of TLSv1 and TLSv1.1, SSLLab’s rating also changed. However, getting an 100 in this section might prevent your clients (who use old devices/OS) from accessing your website.

If your cipher list use TLSv1.2 and above, you’ll get 100% on this section. ( Warning : Older Client will not able to load your site. Depend on your Nginx/OpenSSL version, TLSv1.3 might not work in your system. )

TLSv1.2 only: ssl_protocols TLSv1.2; TLSv1.3+TLSv1.2: ` `ssl_protocols TLSv1.2 TLSv1.3;

If you want to be geeky (only TLSv1.3): ssl_protocols TLSv1.3;``

Currently, all above choices will give you an 100% on cipher section.

Warning : Depends on your OS, Nginx and OpenSSL (or whichever program you build Nginx on), TLSv1.3 might not work. You also need TLSv1.3 specific ciphers in order for TLSv1.3 to work.

P.S. If you can, always try to enable TLSv1.3, doesn’t hurt if you configure it well.

If your cipher list contains TLSv1 or TLSv1.1, your grade will be capped at B for using depreciated protocols. ( This was the best compatibility option, but no longer suggested. More clients who don’t support TLSv1.2 are being old anyway )

ssl_protocols TLSv1.0 TLSv1.1 TLSv1.2;

ssl_protocols TLSv1.0 TLSv1.1 TLSv1.2 TLSv1.3;

Unless necessary, please do not use Protocols older than TLS1.2 since it’s been deprecated.

  • Key Exchange Section

Use 4096-bit RSA or secp256 and higher ECC Certificate (This is a must if you want 100% on Key Exchange)

Example (RSA):
_ certbot --rsa-key-size 4096 -(other-arguments) _

Example: (ECC):

First generate ECC key & CSR.

_Please follow step one and two from this tutorial: _
https://knowledge.symantec.com/support/mpki-for-ssl-support/index?page=content&id=INFO1909

Then use certbot, specify csr
certbot --csr (/ecc-csr-path) -(other-arguments)

Generate 4096 bit (AT LEAST) dhparam file (Ignore it if you don’t want to use dh suites)

cd /etc/ssl/certs (move to folder where you store certs, default is /etc/ssl/certs.)
openssl dhparam -out dhparam.pem 4096 (change 4096 to larger if you want, it’s taking a long time to generate)

Important Note:
_If you are using DH suites and RSA/ECC certificates, ssllab consider the smallest exchange size as the final score. (e.g. If you have 4096 bit RSA and 2048 bit DHparam, you’ll get 90%) _

  • Cipher Strength Section

For cipher strength, you are always suggested to use at least option Intermediate on the config https://ssl-config.mozilla.org, which will gets you around 90% on Cipher strength section.

You’ll need to use ciphers greater than or equal to 256 bit to get 100%

ssl_ciphers TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256: ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES256-GCM-SHA384;

Warning : some client may not able to connect when using those cipher . According to SSLLabissue, there’s no way to get 100% on this section when TLSv1.3 is enabled. OpenSSL will always attempt to add TLS_AES_128_GCM_SHA256 to your configuration.

Using Ciphers >=128 bit for best compatibility (Will get 90% in score)

ssl_ciphers TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:TLS_AES_128_CCM_8_SHA256:TLS_AES_128_CCM_SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;

Warning: The cipher list is generated as of 2/24/2020, please always visit https://ssl-config.mozilla.org to generate a new one (Intermediate Configuration) to avoid issues.

Use ECDH curve >= 256bit ( optional if you are not using ECDHE suite)

ssl_ecdh_curve secp384r1;

  • Get A+ in letter grade

Enable HSTS with long duration (More than 1 week)

HSTS is a header which can force visitor use https (Warning: browser will not connect to your site if there’s no ssl configured and HSTS is enabled)

Entry level hsts (Just satisified A+)
add_header Strict-Transport-Security "max-age=63072000;";

Hsts setup that would require ssl on all subdomains (After visiting your root domain, your non-https subdomain will not be accessable.)
add_header Strict-Transport-Security "max-age=63072000; includeSubdomains;";

Hsts setup that can request to be hardcoded in browser HSTS list (After adding this option, you can apply to be inside hsts preload list, which is a list that was hardcoded into browser, means your non-https site will present an error message to all visitors even if they never visit your base domain before. it’s very hard to be removed from the list)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains;" preload;

  • Other Settings (Suggested but not required)

Enable OCSP Stapling (Optional)
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate your cert path/letsencrypt-full-chain.pem; (the CA&Intermediate CA file for your cert)

Expect CT (Optional)
This is a new header which can tell Browsers if they are trying to find Certificate Transparency (log) (Explained in https://scotthelme.co.uk/a-new-security-header-expect-ct/)
Few Usages:
testing only (With no report uri): add_header Expect-CT "max-age=0";
testing only (With report-uri) (Please follow the link above for report-uri’s usage) : add_header Expect-CT "max-age 0, report-uri https://{$subdomain}.report-uri.com/r/d/ct/reportOnly ";
enforce (go for short duration first): add_header Expect-CT "enforce, max-age 30, report-uri https://{$subdomain}.report-uri.com/r/d/ct/reportOnly ";

Add xframe (Optional)
add_header X-Frame-Options DENY;
Warning: This will deny all frame for your website. For example: WordPress’s update process page will not work properly (show blank.instead of actual progress)

  • Update:
  1. According to @Osiris, remove some unnecessary part of hsts.
  2. Add more clarification, restructure the doc.
  3. Expect CT Header (Prepare for Certificate Transparency)
  • Credit:
  1. https://mozilla.github.io/server-side-tls/ssl-config-generator/
  2. https://github.com/ssllabs/research/wiki/SSL-Server-Rating-Guide
3 Likes

Be careful! Only add “includeSubdomains” if that really is what you want.

If you have a subdomain without HTTPS (for example, a separate device which doesn’t support TLS on a separate subdomain), you won’t be able to access that host if the browser has “seen” the HSTS header with the “includeSubdomains” option!

@stevenzhu Please elaborate some more on the different options you’re suggesting. As noted above about HSTS for example. Also, generating DH parameters isn’t neccessary if the user disables non-ECC DH al together. I would suggest giving the user some well informed options.

@stevenzhu The “includeSubdomains” option is powerful and can be very useful! Sorry if I wasn’t clear, but I didn’t mean it should be removed in total. Just a warning to novice users who just copy/paste everything they see without knowing what it is they’re pasting…

3 Likes

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.

This is… unadvisable, since certs obtained with certbot --csr are not autorenewable (unless smart usage of hooks and shell magic can generate the csr… haven’t tried).

When I did this, I switched the machine to using acme.sh, and I am still bitter its documentation is not even close to being as good as certbot’s.


I had my config on here, this is the upgraded version:

(this will not get you 100-100-100-100 A+, but only 100-100-100-90 A+. read the linked thread to know why :smiley:)

ssl_protocols TLSv1.3 TLSv1.2;
ssl_prefer_server_ciphers off;
ssl_ciphers ECDHE+AESGCM:DHE+AESGCM:ECDHE+ECDSA+AES+SHA256;

ssl_ecdh_curve secp521r1:X448:secp384r1:secp256k1;

ssl_dhparam dhparams.pem;

ssl_stapling on;

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header Content-Security-Policy "upgrade-insecure-requests" always;

ssl_certificate acme.sh/quake/fullchain.pem;
ssl_certificate_key acme.sh/quake/key.pem;

ssl_certificate acme.sh/quake/fullchain-ecc.pem;
ssl_certificate_key acme.sh/quake/key-ecc.pem;

(it includes one cipher that ssllabs marks as weak, it’s on purpose, for client compatibility.)

Well, acme.sh is indeed better than certbot when issuing ecc certificates.
I’ll probably add references to acme.sh in spring break.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.