· Nginx SSL/TLS Security

SSL/TLS Best Security Practises with CentOS and Nginx

Originally this article was going to be about scoring an A+ with Qualys and whilst its a good score to get, I think, it drives the wrong mindset. We should be making security a default thing to configure in every aspect of online life, and not just getting a score with a company or passing PCI Compliance once a quarter.

This article is going to cover a few bits, this has all be tested on Rackspace Cloud Server running CentOS 7 and nginx compiled from source.

TL;DR There is a config sample at the bottom, ready for copy/paste. Just change the server_name and certificate/private locations.

The only reason for the using Nginx compiled from source was for the ability to edit one file before compiling and running, and that was ngx_http_header_filter_module.c file. This file sets the Server name in the HTTP header. Nginx hard sets this value on line 50. If you dont want to change this value, I would recommend adding Nginx repository and using yum for easier update management. There is not real reason to do this, other than it looks cool.

[[email protected] ~]# curl -I https://aaron.mehar.me
HTTP/1.1 200 OK
Server: Aarons Server
Date: Thu, 12 Mar 2015 20:43:38 GMT

Nginx Server block

The nginx block (vhost in Apache) for HTTP simply redirects all traffic to HTTPS.

server {
   listen 80;
   server_name aaron.mehar.me;
   return 301 https://$server_name$request_uri;

Protocols and Ciphers

Determining the protocols to set it pretty simple, disable all SSL versions and only use TLS, I would even disable TLSv1 but the adoption and support of TLS1.1 and 1.2 is still quite low. In Nginx, we can simply specific which protocols to set.

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

Cipher suits are a bit more difficult. Normally they should be defined by your user base and their browser versions, essentially to make like easy and this article short, I have included two sets below, the sort suite is very strong and basically will not allow ‘moderately’ old clients to connect but the longer will is a but more forgiving but still secure. Ciphers can be a big topic and I plan on writing an article for them.

ssl_ciphers ‘AES128+EECDH:AES128+EDH’;

Mandatory Discards

aNULL contains non-authenticated Diffie-Hellman key exchanges, that are subject to Man-In-The-Middle (MITM) attacks eNULL contains null-encryption ciphers (cleartext) EXPORT are legacy weak ciphers that were marked as exportable by US law DES contains ciphers that use the deprecated Data Encryption Standard MD5 contains all the ciphers that use the deprecated message digest 5 as the hashing algorithm SSLv2 contains all ciphers that were defined in the old version of the SSL standard, now deprecated RC4 contains ciphers that use the deprecated ARCFOUR algorithm

Diffie-Hellman Parameter Certificate

Nginx from version 1.4.4 uses OpenSSL to provide input parameters to Diffie-Hellman, this means getting a 1024-Bit key for the exchange. Which means Diffie-Hellman-Ephemeral clients will use a weaker key-exchange than non-ephemeral Diffie-Hellma clients. We can generate a certificate and define the parameter in the nginx configuration and raise this to 4096. This raises the ‘key exchange’ rating with Qualys from 90 to 100

Create the certificate
openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096
Add it to your configuration
ssl_dhparam /etc/ssl/certs/dhparam.pem;

SSL Compression aka ‘CRIME’

CRIME is based on SSL Compression and mitigation is to simply disable it server side. SSL Compressions is turned off by default in Nginx 1.1.6+ when used with OpenSSL 1.0.0+ and Nginx 1.3.1 is older versions of OpenSSL. If you are running these versions, please consider updating to them, Nginx is currently at 1.6.2 and OpenSSL 1.0.1L

OSCP Stapling

When a connection is made to a server, the client should check the certificate is still valid, it does this by either using a CRL (Certificate Revocation List) or an OSCP (Online Certificate Status Protocol). The issue with a CRL is they have grown very large and clients must download the whole list and check it, OSCP is much quicker as it only check for the certificate it needs but this means adding a 3rd party the mix which can cause latency and the potential for failure. Browsers will fail silently if no response is received in a timely manner. This reduces security, by allowing an attacker to DoS an OCSP responder to disable the validation. OSCP Staling allows a cached version on the server, normally valid for 48 hours, mitigating the need for the OSCP responder. Configuration is simple, see the example below

ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/pki/tls/certs/aaron.mehar.me.ca;


HSTS should be enabled when possible, its sends a header to the browser that all future connections are to made over HTTPS. HSTS (HTTP Strict Transport Security) is best for SSL-stripping man-in-the-middle attacks. Be warned, the header last for as long as the age you set, to be sure that you want the entire site in HTTPS before setting. We configure this in the nginx configuration;

add_header Strict-Transport-Security “max-age=63072000; includeSubDomains”;

You should also enable the HTTP Public Key Pinning Extension. HPKP is a mechanism for sites to specify which certificate authorities have issued valid certs for that site, and for user-agents to reject TLS connections to those sites if the certificate is not issued by a known-good CA. Public key pinning prevents man-in-the-middle attacks due to rogue CAs not on the site’s list. You should have two ideally, 1 against your certificate and one against the Intermediate CA, just in case you need to revoke/reissue the certificate.

openssl x509 -noout -in aaron.mehar.me.crt -pubkey | openssl asn1parse -noout -inform pem -out public.key;openssl dgst -sha256 -binary public.key | openssl enc -base64
openssl x509 -noout -in aaron.mehar.me.ca -pubkey | openssl asn1parse -noout -inform pem -out public.key;openssl dgst -sha256 -binary public.key | openssl enc -base64

The results will look similar to the below.


We take the output and add it to the Nginx configuration file.

add_header Public-Key-Pins ‘pin-sha256=”klO23nT2ehFDXCfx3eHTDRESMz3asj1muO+4aIdjiuY=” pin-sha256=”grX4Ta9HpZx6tSHkmCrvpApTQGo67CYDnvprLg5yRME=” ;max-age=2592000; includeSubDomains’;

Updating Headers

There are some extra header that can be set to aid in security. X-Frame-Options DENY so the page cannot be displayed in a frame, regardless of the site attempting to do so. X-Content-Type-Options nosniff prevents the browser from doing MIME-type sniffing and Content-Security-Policy prevents cross-site scripting (XSS) and related attacks, essentially a whitelist of where content can be loaded from. X-XSS-Protection nables the Cross-site scripting (XSS) filter built into most recent web browsers. These are simply set in the Nginx configuration.

add_header X-XSS-Protection “1; mode=block”;
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header Content-Security-Policy “default-src ‘self’; script-src ‘self’ ‘unsafe-inline’ https://ssl.google-analytics.com; style-src ‘self’ ‘unsafe-inline’ https://fonts.googleapis.com; img-src ‘self’ ‘unsafe-inline’ https://secure.gravatar.com; font-src ‘self’ ‘unsafe-inline’ data: https://fonts.gstatic.com”;

Other security practises

Prefer Cipher sets the rule so the server dictates which ciphers to use and in which order, so browsers or MITM attacks do not set the Ciphers to use. SSL Sessions allow multiple connections to use the same key data to calculate encryption keys for the connection instead of performing a full negotiation to determined the encryption keys. The example below is sharing a 10MB cache which is good for about 4,000 sessions.

ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;


Online security is an ongoing task, keeping up-to-date with the latest vulnerabilities and Ciphers and all sorts, BUT some quick and easy changes to your webserver can make your server and user data as secure as possible. Now use the SSL Labs test to see if you get a nice A+. And, of course, have a safe, strong and future proof SSL configuration!

Just remember to config test and restart Nginx.

Check the config first: nginx -t then restart: systemctl restart nginx.service

I have placed an example of the configuration file here to see

  • LinkedIn
  • Tumblr
  • Reddit

Aaron Mehar

Berkshire, UK