Skip to content

Instantly share code, notes, and snippets.

@konklone
Last active August 8, 2023 08:39
Star You must be signed in to star a gist
Save konklone/6532544 to your computer and use it in GitHub Desktop.
nginx TLS / SSL configuration options for konklone.com
# Basically the nginx configuration I use at konklone.com.
# I check it using https://www.ssllabs.com/ssltest/analyze.html?d=konklone.com
#
# To provide feedback, please tweet at @konklone or email eric@konklone.com.
# Comments on gists don't notify the author.
#
# Thanks to WubTheCaptain (https://wubthecaptain.eu) for his help and ciphersuites.
# Thanks to Ilya Grigorik (https://www.igvita.com) for constant inspiration.
server {
listen 80;
server_name konklone.com;
return 301 https://$host$request_uri;
}
server {
# 'http2' requires nginx 1.9.5+. If using older nginx, replace with 'spdy'.
listen 443 ssl http2;
server_name konklone.com;
# Path to certificate and intermediates, *omitting* the root.
ssl_certificate /path/to/example.com.chained.crt;
# Path to private key used to create certificate.
ssl_certificate_key /path/to/example.com.key;
# HTTP Strict Transport Security: tells browsers to require https:// without first checking
# the http:// version for a redirect. Warning: it is difficult to change your mind.
#
# max-age: length of requirement in seconds (31536000 = 1 year)
# includeSubdomains: force TLS for *ALL* subdomains (remove if this is not what you want)
# preload: indicates you want browsers to ship with HSTS preloaded for your domain.
#
# Submit your domain for preloading in browsers at: https://hstspreload.appspot.com
add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains; preload';
# If you won't/can't turn on HTTPS for *all* subdomains, use this simpler version:
# add_header Strict-Transport-Security 'max-age=31536000';
ssl_prefer_server_ciphers on;
# This requires strong forward secrecy (ECDHE) for all connections.
# However, it blocks IE8+XP and Android 2.3.
ssl_ciphers 'kEECDH+ECDSA+AES128 kEECDH+ECDSA+AES256 kEECDH+AES128 kEECDH+AES256 +SHA !aNULL !eNULL !LOW !MD5 !EXP !DSS !PSK !SRP !kECDH !CAMELLIA !RC4 !SEED';
# Uncomment to require strong forward secrecy (ECDHE) in most clients, with a
# non-FS exception (DES-CBC3-SHA) for IE8/XP, and plain DHE for Android 2.3 users.
# ssl_ciphers 'kEECDH+ECDSA+AES128 kEECDH+ECDSA+AES256 kEECDH+AES128 kEECDH+AES256 kEDH+AES128 kEDH+AES256 DES-CBC3-SHA +SHA !aNULL !eNULL !LOW !MD5 !EXP !DSS !PSK !SRP !kECDH !CAMELLIA !RC4 !SEED';
# Allows all modern and legacy clients to connect over TLS.
ssl_protocols TLSv1.2 TLSv1.1 TLSv1;
# Uncomment for only the latest TLS, if you can drop IE8-IE10 and Android 4.3.
# ssl_protocols TLSv1.2;
# Turn on session resumption, using a 10 min cache shared across nginx processes,
# as recommended by http://nginx.org/en/docs/http/configuring_https_servers.html
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
keepalive_timeout 70;
# OCSP stapling: nginx will poll the CA for signed OCSP responses, and
# send them to clients so clients don't make their own OCSP calls.
#
# The ssl_trusted_certificate is a chain of intermediates *including* the
# root certificate, and *excluding* the cert for your domain.
#
# See https://sslmate.com/blog/post/ocsp_stapling_in_apache_and_nginx
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=86400;
resolver_timeout 10;
ssl_trusted_certificate /path/to/example.com.chain+root.crt;
}
@parkr
Copy link

parkr commented Sep 23, 2013

❤️

Copy link

ghost commented Sep 26, 2013

Nitpick: seems you have an unnecessary redirect when forcing HTTPS:

$ curl -sIL http://www.konklone.com | grep HTTP
HTTP/1.1 301 Moved Permanently
HTTP/1.1 301 Moved Permanently
HTTP/1.1 200 OK

This is what I usually use when forcing non-www HTTPS:

# /etc/nginx/sites-available/example.com
server {
    listen [::]:80;
    listen [::]:443 ssl;
    server_name www.example.com;
    rewrite ^ https://example.com$request_uri? permanent;
}
server {
    listen [::]:80;
    listen [::]:443 ssl;
    server_name example.com;
    if ($ssl_protocol = "") {
            rewrite ^ https://$server_name$request_uri? permanent;
    }
    root /srv/example.com;
    # etc...
}

@konklone
Copy link
Author

Good point, but I think this won't be very common - I never use the www, never have used it, and so it's unlikely the extra redirect will affect most people. It's easier for me, maintenance-wise, to keep those redirects (old domains, needless www, etc.) in one place, and my SSL redirect for my primary domain in another.

@jonnybarnes
Copy link

I was wondering why PFS wasn't working for me. I'm running a couple of HTTPS sites on my VPS (one beeing https://piwik.domian.com for example). To get forward secrecy for the main domain I changed the listen line on the https server block and added the default_server directive:

server {
    listen 443 ssl spdy default_server;
    server_name domain.com;
    ...
}

@konklone
Copy link
Author

I just updated the gist to change my ciphersuites to:

ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES256-SHA:RC4-SHA:AES256-GCM-SHA384:AES256-SHA256:CAMELLIA256-SHA:ECDHE-RSA-AES128-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:CAMELLIA128-SHA;

This cut out the non-elliptic-curve based ciphers in DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:, and raised the Key Exchange score on SSL Labs to 90.

@konklone
Copy link
Author

konklone commented Mar 2, 2014

I just added OCSP stapling, SPDY header compression, and an SSL buffer size directive (this one is only in nginx 1.5.9+).

@jonnybarnes
Copy link

Is /path/to/unified.crt and /path/to/all-certs-in-chain.crt expected to be different? For StartSSL should the OCSP cert be a concatenation of your own cert + http://www.startssl.com/certs/sub.class1.server.ca.pem +http://www.startssl.com/certs/ca.pem?

@konklone
Copy link
Author

@jonnybarnes - my understanding is the OCSP cert needs to include the root cert (ca.pem). But that root cert is optional in what you serve to users, because StartSSL's is already in every browser's trust store, so it saves some bandwidth and processing time.

That said, I've been thinking about switching back to the full cert for both, just to make my configuration simpler, clearer, and easier to manage. I'll probably do that (and then switch my HTTPS guide back to recommend concatenating the root cert, as it originally did).

Also, I have a blog post in the works on OCSP stapling that'll make this issue more clear.

@konklone
Copy link
Author

I just posted a major update, with help from @wubthecaptain. I also updated the comments and better explained things. SSL Labs score is better than ever, with a 95 now for protocol support, and enforced forward secrecy for all modern browsers and clients (with a non-FS carve-out for IE8/XP).

@konklone
Copy link
Author

Note: according to SSL Labs, the latest update excludes three kinds of clients:

  • Yandexbot - requires SSLv3
  • IE6 on XP - requires SSLv3
  • Java 6 - can't handle DH parameters greater than 1024 bits

If this isn't okay, you can address any of them by commenting out the relevant section. To include SSLv3, comment out the ssl_protocols line. To reduce the DH parameters to 1024 bits, comment out the ssl_dhparam line.

@konklone
Copy link
Author

konklone commented Sep 1, 2014

Updated the guide to default to using includeSubdomains and preload for the HSTS header, and linked to Chrome's HSTS Preload submission tool. Included language people can fall back to if HSTS with subdomains isn't for them.

@jonnybarnes
Copy link

So I can't get OCSP stapling for jonnybarnes.uk working properly on ssllabs.com, however it does respond correctly to openssl s_client connect jonnybarnes.uk:443 -CAfile /path/to/cafile.crt -tls1 -tlsextdebug -status. Some observations, my ssl_trusted_certificate file is of the form:

=====BEGIN CERTIFICATE=====
INTERMEDIATE CERT
=====END CERTIFICATE=====
=====BEGIN CERTIFICATE=====
ROOT CERT
=====END CERTIFICATE=====

Also, if you are hosting more than one ssl site on nginx, there needs to be one set as the default_server and that needs to have stapling enabled. Otherwise none of the other ssl sites willl have stapling enabled, regardless of configuration.

@kcivey
Copy link

kcivey commented Oct 16, 2014

Thanks. I finally did some of this and plan to do most of the rest soon.

@StefanWallin
Copy link

For a strategy to use this with SNI-hosts, head on over to my fork

@jonnybarnes
Copy link

Is it worth adding tcp_nodelay for nginx 1.7.8+ (see entry 6)? I have no idea.

@jpmckinney
Copy link

How did you select your ciphersuite? Here's the cargo-cult:

https://www.digicert.com/ssl-support/ssl-enabling-perfect-forward-secrecy.htm lists three with a rationale for each:

Includes RC4 (because it resists to BEAST):
EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS

Excludes RC4 (because it has weaknesses):
EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4

Uses RC4 as last resort (e.g. for old TLS versions):
EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS +RC4 RC4

All three are cargo-culted to https://community.qualys.com/blogs/securitylabs/2013/08/05/configuring-apache-nginx-and-openssl-for-forward-secrecy and the last one is at http://axiacore.com/blog/enable-perfect-forward-secrecy-nginx/

https://wiki.mozilla.org/Security/Server_Side_TLS#Recommended_configurations has three configurations with explanation.

https://raymii.org/s/tutorials/Strong_SSL_Security_On_Apache2.html has one configuration with explanation.

@konklone
Copy link
Author

@jpmckinney I don't remember where I get my first draft of them, but they've evolved over time as I've read different things, and as people wrote to me directly with suggestions (wubthecaptain in particular was super helpful.). So it's no longer something I've just copy-pasted from a blog post.

The set I have now ensures strong forward secrecy on everything but IE8+XP, and for that there's a working non-RC4 carveout, in the form of 3DES:
https://www.ssllabs.com/ssltest/analyze.html?d=konklone.com&latest

Some of this is cargo-culted back to Qualys, I'm sure -- I don't know why 3DES is stronger than RC4 offhand -- but I'm comfortable with the empirical evidence that various clients select different ciphers that offer different guarantees. My priorities are: forward secrecy as much as possible, avoid RC4 as much as possible. I think I've got that.

@jpmckinney
Copy link

Cargo-cult isn't necessarily bad - actually, in this case, it makes it harder to evaluate whether your ciphersuite is better or worse than others' :) The sslmate mkconfig recommends the Mozilla "modern" profile, though that cuts out a lot of old browsers that many people still use. I'm using https://wiki.mozilla.org/Security/Server_Side_TLS#Intermediate_compatibility_.28default.29 for now.

@cubiclesoft
Copy link

The apparent 'default_server' requirement in nginx is looking more and more like a bug to me. I have a self-signed cert on a default_server SSL server. This is 100% intentional to disable all non-SNI clients. The certificate itself is carefully crafted to let the user know that their web browser is broken and that they need to upgrade to something reasonable and modern. It is also signed for zero domains. Obviously, there is no OCSP stapling server for this cert and there is no content for the website either should the user choose to "add an exception" and/or "proceed anyway". The only option for the user is to upgrade, which is precisely what should happen anyway because the outdated browsers being excluded are insecure and need to stop being used.

However, nginx seems to require the 'default_server' to have OCSP stapling enabled. At best, it's a broken dependency requirement. That is, I have to set up an OCSP server to host my only self-signed cert (and probably regenerate the cert too) to get nginx to do OCSP stapling for the other 'server' entries. Really? That's a waste of web server system resources to "fix" this problem and makes OCSP stapling more trouble than it is worth. But it's more likely a bug in the nginx SSL module itself where this very scenario wasn't conceived of. However, intentionally disabling non-SNI clients should be the defacto standard approach to SSL and a server operator should be able to just set stapling directives on the exact 'server' entries that they want it enabled on. The only valid conclusion that can be drawn is that OCSP stapling support is half-baked, barely functional tech in nginx.

@Pilvinen
Copy link

Pilvinen commented Oct 9, 2015

I followed your instructions and copied the above nginx.conf and replaced the one in /etc/nginx/.
Then I reloaded with "service nginx restart"
And ran: "nginx -t" and then I get this:

nginx: [emerg] "server" directive is not allowed here in /etc/nginx/nginx.conf:10
nginx: configuration file /etc/nginx/nginx.conf test failed

And no https for me. What now?

@konklone
Copy link
Author

@Pilvinen That's an issue somewhere in your nginx.conf file. The above should be included inside a server block somewhere, included dynamically. See https://github.com/fisma-ready/nginx for an example.

@laike9m
Copy link

laike9m commented Oct 24, 2015

Buffer size of 1400 bytes fits in one MTU.

I haven't found any documentation saying this is the best practice.

Here's what's in Nginx's doc:

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:
ssl_buffer_size 4k;

Did you do some test before setting it to 1400?

@konklone
Copy link
Author

@laike9m I just blindly copied it from @igrigorik's config for istlsfastyet.com, as I blindly trust him on all things regarding TLS performance. =)

https://github.com/igrigorik/istlsfastyet.com/blob/master/nginx/nginx.conf

@laike9m
Copy link

laike9m commented Oct 26, 2015

I see. Tests on my website shows setting buffer size from 16k(default) to 1400 reduced TTFB, but increased page download time.

@max-mapper
Copy link

1492 is an ethernet jumbo frame size

@JanKanis
Copy link

JanKanis commented Jan 7, 2016

@cubiclesoft or others who have the same problem: The ssl_stapling not working if it is not enabled in the default server seems to be this bug: https://trac.nginx.org/nginx/ticket/810. The actual bug is in openssl, fixed with OpenSSL 1.0.0m/1.0.1g/1.0.2, so check your openssl version (nginx -V)

@KireinaHoro
Copy link

@konklone you're scoring an F at ssllabs.

 OpenSSL Padding Oracle vuln.
(CVE-2016-2107) Yes  INSECURE (more info). 

@Peneheals
Copy link

Peneheals commented Nov 2, 2016

typo at resolver section.

it has to be resolver 8.8.8.8 8.8.4.4 valid=86400s; according to the nginx manual.

and DES-CBC3-SHA is affected by BEAST, so I suggest disabling here.

@C0nw0nk
Copy link

C0nw0nk commented Sep 24, 2018

Should use a privacy respecting and worlds fastest DNS resolver instead of Google's 8.8.8.8 and 8.8.4.4 IPv4 resolver what is slow and Does not respect privacy or security. Cloudflares resolver also includes both IPv4 IPv6 resolver and alternative fallback DNS resolver alternative resolver for is for IPV4 is 1.0.0.1 and IPv6 2606:4700:4700::1001 as can be found on their website privacy security and speed comes first.

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

@jessuppi
Copy link

Fantastic SSL config and discussion here, thanks guys... we used some of your suggestions in our SlickStack settings:

https://github.com/littlebizzy/slickstack/blob/master/nginx/nginx-conf.txt

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