Instantly share code, notes, and snippets.

Embed
What would you like to do?
Best nginx configuration for improved security(and performance). Complete blog post here http://tautt.com/best-nginx-configuration-for-security/
# to generate your dhparam.pem file, run in the terminal
openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048
# read more here http://tautt.com/best-nginx-configuration-for-security/
# don't send the nginx version number in error pages and Server header
server_tokens off;
# config to don't allow the browser to render the page inside an frame or iframe
# and avoid clickjacking http://en.wikipedia.org/wiki/Clickjacking
# if you need to allow [i]frames, you can use SAMEORIGIN or even set an uri with ALLOW-FROM uri
# https://developer.mozilla.org/en-US/docs/HTTP/X-Frame-Options
add_header X-Frame-Options SAMEORIGIN;
# when serving user-supplied content, include a X-Content-Type-Options: nosniff header along with the Content-Type: header,
# to disable content-type sniffing on some browsers.
# https://www.owasp.org/index.php/List_of_useful_HTTP_headers
# currently suppoorted in IE > 8 http://blogs.msdn.com/b/ie/archive/2008/09/02/ie8-security-part-vi-beta-2-update.aspx
# http://msdn.microsoft.com/en-us/library/ie/gg622941(v=vs.85).aspx
# 'soon' on Firefox https://bugzilla.mozilla.org/show_bug.cgi?id=471020
add_header X-Content-Type-Options nosniff;
# This header enables the Cross-site scripting (XSS) filter built into most recent web browsers.
# It's usually enabled by default anyway, so the role of this header is to re-enable the filter for
# this particular website if it was disabled by the user.
# https://www.owasp.org/index.php/List_of_useful_HTTP_headers
add_header X-XSS-Protection "1; mode=block";
# with Content Security Policy (CSP) enabled(and a browser that supports it(http://caniuse.com/#feat=contentsecuritypolicy),
# you can tell the browser that it can only download content from the domains you explicitly allow
# http://www.html5rocks.com/en/tutorials/security/content-security-policy/
# https://www.owasp.org/index.php/Content_Security_Policy
# I need to change our application code so we can increase security by disabling 'unsafe-inline' 'unsafe-eval'
# directives for css and js(if you have inline css or js, you will need to keep it too).
# more: http://www.html5rocks.com/en/tutorials/security/content-security-policy/#inline-code-considered-harmful
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://ssl.google-analytics.com https://assets.zendesk.com https://connect.facebook.net; img-src 'self' https://ssl.google-analytics.com https://s-static.ak.facebook.com https://assets.zendesk.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://assets.zendesk.com; font-src 'self' https://themes.googleusercontent.com; frame-src https://assets.zendesk.com https://www.facebook.com https://s-static.ak.facebook.com https://tautt.zendesk.com; object-src 'none'";
# redirect all http traffic to https
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name .forgott.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name .forgott.com;
ssl_certificate /etc/nginx/ssl/star_forgott_com.crt;
ssl_certificate_key /etc/nginx/ssl/star_forgott_com.key;
# enable session resumption to improve https performance
# http://vincent.bernat.im/en/blog/2011-ssl-session-reuse-rfc5077.html
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
ssl_dhparam /etc/nginx/ssl/dhparam.pem;
# enables server-side protection from BEAST attacks
# http://blog.ivanristic.com/2013/09/is-beast-still-a-threat.html
ssl_prefer_server_ciphers on;
# disable SSLv3(enabled by default since nginx 0.8.19) since it's less secure then TLS http://en.wikipedia.org/wiki/Secure_Sockets_Layer#SSL_3.0
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
# ciphers chosen for forward secrecy and compatibility
# http://blog.ivanristic.com/2013/08/configuring-apache-nginx-and-openssl-for-forward-secrecy.html
ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
# enable ocsp stapling (mechanism by which a site can convey certificate revocation information to visitors in a privacy-preserving, scalable manner)
# http://blog.mozilla.org/security/2013/07/29/ocsp-stapling-in-firefox/
resolver 8.8.8.8 8.8.4.4;
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/nginx/ssl/star_forgott_com.crt;
# config to enable HSTS(HTTP Strict Transport Security) https://developer.mozilla.org/en-US/docs/Security/HTTP_Strict_Transport_Security
# to avoid ssl stripping https://en.wikipedia.org/wiki/SSL_stripping#SSL_stripping
# also https://hstspreload.org/
add_header Strict-Transport-Security "max-age=31536000; includeSubdomains; preload";
# ... the rest of your configuration
}
@ryankearney

This comment has been minimized.

ryankearney commented Jan 2, 2014

The Strict-Transport-Security header needs to be moved inside the http block with the ssl listen statement or you risk sending Strict-Transport-Security headers over HTTP sites you may also have configured on the server.

Additionally, the rewrite for the http server block should be a return 301 instead. See this Nginx documentation for more details: http://wiki.nginx.org/Pitfalls#Taxing_Rewrites

@plentz

This comment has been minimized.

Owner

plentz commented Jan 11, 2014

@ryankearney updated, thanks :)

@barrybingo

This comment has been minimized.

barrybingo commented Jan 11, 2014

ssl_session_timeout 5m; <- Should this appear twice in the config?

@tzuryby

This comment has been minimized.

tzuryby commented Feb 12, 2014

@barrybingo that is probably a typo. once is just enough.

@altonius

This comment has been minimized.

altonius commented Feb 16, 2014

As you're already redirecting all HTTP traffic to HTTPS, have you looked into using SPDY to increase performance?

nginx 1.5.10 (mainline) has SPDY 3.1 support.

Also if you're running nginx as a proxy have you considered turning off the X-Powered-By header by adding the following line to the server config section.

proxy_hide_header X-Powered-By;

@RoldanLT

This comment has been minimized.

RoldanLT commented Feb 19, 2014

How to force redirect all request to www. version?
Here's my current config:

server {
server_name domain.com www.domain.com;
return 301 https://www.domain.com$request_uri;
}

server {
listen 443 ssl spdy;
server_name www.domain.com;

domain.com to https://www.domain.com = Works
www.domain.com to https://www.domain.com = Works
https://domain.com to https://www.domain.com = Not working

@plentz

This comment has been minimized.

Owner

plentz commented Feb 20, 2014

@barrybingo not really, thanks!

@denji

This comment has been minimized.

@johnantoni

This comment has been minimized.

johnantoni commented Apr 15, 2014

nice work

@vincentclee

This comment has been minimized.

vincentclee commented Apr 19, 2014

RoldanLT

ssltunnel.net to https://www.ssltunnel.net = Works
www.ssltunnel.net to https://www.ssltunnel.net = Works
https://ssltunnel.net to https://www.ssltunnel.net = Works

#ssltunnel.net
server {
        listen 80;
        server_name ssltunnel.net www.ssltunnel.net;
        rewrite ^ https://www.ssltunnel.net$request_uri? permanent;
}
server {
        listen 443 ssl spdy;
        server_name ssltunnel.net;
        rewrite ^ https://www.ssltunnel.net$request_uri? permanent;

        ssl on;
        ssl_certificate ssltunnel.net.crt;
        ssl_certificate_key ssltunnel.net.key;
}
server {
        listen 443 ssl spdy;
        server_name www.ssltunnel.net
...
@RoldanLT

This comment has been minimized.

RoldanLT commented Apr 20, 2014

Thanks vincentclee!
I'm using this now and it works :)

server {
listen 80;
server_name phcorner.net www.phcorner.net;
return 301 https://www.phcorner.net$request_uri;
}

server {
listen 443 ssl spdy;
server_name phcorner.net;
return 301 https://www.phcorner.net$request_uri;

    ssl on;
ssl_certificate /ssl-unified.crt;
ssl_certificate_key /net.key;

}

server {
listen 443 ssl spdy;
server_name www.phcorner.net;
...

@toddlahman

This comment has been minimized.

toddlahman commented Sep 13, 2014

The browser will only listen to the Strict-Transport-Security header if the connection was established via HTTPS. The first time the visitor connects to the website using HTTP, the visitor needs to be redirected using a 301 redirect. On subsequent requests, even it the connection uses HTTP, HSTS forces the browser to use HTTPS for connecting to a particular website instead of HTTP. Even if a user types in a http:// URL, the browser will automatically correct the URL and will connect to https://.

It appears the more correct way to implement HSTS in Nginx is as so:

map $scheme $hsts_header {
    https   max-age=31536000;
}

server {
    listen  80;
    listen  443 ssl;

    add_header Strict-Transport-Security $hsts_header;
}

http://trac.nginx.org/nginx/ticket/289

Most examples use the following to implement HSTS, which does not follow the RFC.

add_header Strict-Transport-Security "max-age=31536000; includeSubdomains;";

@bbigras

This comment has been minimized.

bbigras commented Sep 18, 2014

@toddlahman : Shouldn't we avoid to set the header for http (port 80) ?

An HSTS Host MUST NOT include the STS header field in HTTP responses conveyed over non-secure transport.

https://tools.ietf.org/html/rfc6797

@tisdall

This comment has been minimized.

tisdall commented Oct 15, 2014

In nginx >= 1.7.5 you should have (note the added "always"):

add_header Strict-Transport-Security max-age=31536000 always;

This forces the header to always be added regardless of the http code which is what is wanted for this type of header.

@wyrmiyu

This comment has been minimized.

wyrmiyu commented Dec 2, 2014

I'm a bit curious, you seem to use ssl_trusted_certificate with the same wildcard-certificate that you use with ssl_certificate but Nginx documentation[1] specifies that ssl_trusted_certificate

Specifies a file with trusted CA certificates in the PEM format used to verify client certificates and OCSP responses if ssl_stapling is enabled.

The file should be a certificate bundle containing root and intermediate CAs.

[1] http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_trusted_certificate

@chrisnew

This comment has been minimized.

chrisnew commented Dec 9, 2014

Use following ciphers to exclude RC4.

ssl_ciphers "EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA256:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EDH+aRSA+AESGCM:EDH+aRSA+SHA256:EDH+aRSA:EECDH:!aNULL:!eNULL:!MEDIUM:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS:!RC4:!SEED";
@iamkingsleyf

This comment has been minimized.

iamkingsleyf commented Jan 9, 2015

i am getting access denied error

@paskal

This comment has been minimized.

paskal commented Jan 14, 2015

From my fork, you may want to include it as well:

# enabling Public Key Pinning Extension for HTTP (HPKP)
# https://developer.mozilla.org/en-US/docs/Web/Security/Public_Key_Pinning
# to generate use on of these:
# $ openssl rsa -in my-website.key -outform der -pubout | openssl dgst -sha256 -binary | base64
# $ openssl req -in my-website.csr -pubkey -noout | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary | base64
# $ openssl x509 -in my-website.crt -pubkey -noout | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary | base64
add_header Public-Key-Pins 'pin-sha256="base64+info1="; max-age=31536000; includeSubDomains'; 
@guedressel

This comment has been minimized.

guedressel commented Mar 11, 2015

@chrisnew thanks for the RC4-less ciphers.

@doriancosentino

This comment has been minimized.

doriancosentino commented Mar 16, 2015

hello!

more one question about this

when try access https://myip not redirect to my https://mysite.com

how to redirect my ip to domian name?

works http my ip to dimain but https not

thanks

@mhcerri

This comment has been minimized.

mhcerri commented Mar 18, 2015

If you add a single 'add_header' inside the server directive (in your case, for HSTS), it will override all the 'add_header's that you've defined outside.

@bryfry

This comment has been minimized.

bryfry commented Mar 24, 2015

EDH+aRSA is now considered weak http://imgur.com/C5fKr38

I updated my ciphers string to:

EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH RSA+AESGCM RSA+AES !RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS
@ericrange

This comment has been minimized.

ericrange commented Mar 28, 2015

maybe you could add keepalive_timeout 70;
(http://nginx.org/en/docs/http/configuring_https_servers.html)

@tierra

This comment has been minimized.

tierra commented Jul 3, 2015

@bryfry - They're considered weak in your case because you're still using a common Diffie-Hellman group with your ciphers, not because the cipher itself is weak. You don't need to disable EDH+aRSA ciphers, you just need to generate a new unique DH group, and configure it with your web server:
https://weakdh.org/sysadmin.html

@oliversalzburg

This comment has been minimized.

oliversalzburg commented Aug 14, 2015

Why are the unsafe- variants allowed in the CSP? Also, the URL for Google Analytics content is https://www.google-analytics.com now.

@t1gor

This comment has been minimized.

t1gor commented Sep 14, 2015

@oliversalzburg: +1 for google analytic url.

@AndrewBelt

This comment has been minimized.

AndrewBelt commented Oct 8, 2015

server_tokens off; should probably also go in the host on port 80, although I haven't checked if it actually responds with server tokens on the redirect.

@rimhoffd

This comment has been minimized.

rimhoffd commented Dec 25, 2015

More HSTS updates:
add_header Strict-Transport-Security "max-age=31622400; includeSubDomains; preload";

new requirements from Chromium team on getting in the preload list of browsers (and a core point of HSTS)
https://hstspreload.appspot.com/

@nkukard

This comment has been minimized.

nkukard commented Jan 3, 2016

Having TLSv1 enabled fails the latest trustwave.com PCI scan btw.

@lvjurz

This comment has been minimized.

lvjurz commented Feb 2, 2016

Hi,

you should not use '$host' variable in redirect, because it will redirect you to anything that user/attacker will ask for 'someevilhost.com' in this case. Instead make sure you set your server_name correctly (i.e. myhost.com) and then use '$server_name' instead. Alternatively you can use FQDN in redirect and append $request_uri ('return 301 https://myhost.com$request_uri;').

Below is example of what happens when $host is used.

Request

Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET /login HTTP/1.0
Host: someevilhost.com

Response:

HTTP/1.1 301 Moved Permanently
Server: nginx/1.4.6 (Ubuntu)
Date: Tue, 02 Feb 2016 12:19:59 GMT
Content-Type: text/html
Content-Length: 193
Connection: close
Location: http://someevilhost.com/login

@MichaeMimouni

This comment has been minimized.

MichaeMimouni commented Feb 5, 2016

This param

Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits

ssl_dhparam /etc/nginx/ssl/dhparam.pem;
Set to 2048 is very cpu consumer.
On an octo-core Intel(R) Xeon(R) CPU X3450 @ 2.67GHz, 4 nginx workers all at 100% cpu.
When removing the ssl_dhparam, the cpu is used normally.
I think it could simply be lower than 2048.

@ashabada

This comment has been minimized.

ashabada commented Feb 17, 2016

It will only consume 100% cpu while generating 'dhparam.pem'.

Also "All versions of nginx as of 1.4.4 rely on OpenSSL for input parameters to Diffie-Hellman (DH). Unfortunately, this means that Ephemeral Diffie-Hellman (DHE) will use OpenSSL's defaults, which include a 1024-bit key for the key-exchange. Since we're using a 2048-bit certificate, DHE clients will use a weaker key-exchange than non-ephemeral DH clients. We need to generate a stronger DHE parameter."

There is a great post explaining various settings indepth https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html

@gertvdijk

This comment has been minimized.

gertvdijk commented Feb 17, 2016

As indicated by @mhcerri, the add_header directives in the parent scope are not effective for HTTPS requests.

Please see my blog post for a further explanation: Nginx add_header configuration pitfall.

@jeremysears

This comment has been minimized.

jeremysears commented Mar 21, 2016

This configuration uses very weak protocols and cipher suites. I recommend using this generator to get the latest recommended NGINX configs:
https://mozilla.github.io/server-side-tls/ssl-config-generator/

@wellington1993

This comment has been minimized.

wellington1993 commented Mar 30, 2016

Thanks in a lot!

@conradwt

This comment has been minimized.

conradwt commented May 6, 2016

@ryankearney Should all these header statements be added to the sever block because it seems that they are causing issues for other server specific files?

@wget

This comment has been minimized.

wget commented May 10, 2016

@conradwt I'm experiencing the same issues. When either doing a test either with nginx -t -c /my/nginx.conf/ or by restarting nginx, the latter complains with error message like:

"server_tokens" directive is not allowed here in /my/nginx.conf
"add_header" directive is not allowed here in /my/nginx.conf

@rgeraads

This comment has been minimized.

rgeraads commented Jun 7, 2016

@dscso

This comment has been minimized.

dscso commented Jun 10, 2016

You should also listen on IPv6...

listen [::]:443 ssl;
@fotonobile

This comment has been minimized.

fotonobile commented Jun 27, 2016

@Revokee

This comment has been minimized.

Revokee commented Jun 30, 2016

@fotonobile actually you need to update your OpenSSL to fix that (I had to restart my server as well)

@morsik

This comment has been minimized.

morsik commented Jul 2, 2016

Just included those values inside my nginx 1.11 config.
add_header doesn't works when inside http section (but docs says it should, and nginx doesn't tell me bad things about this, so strange). Moved into server section caused to serve those headers correctly.

Strange.

@sander

This comment has been minimized.

sander commented Jul 15, 2016

@morsik did you have additional add_headers in server?

There could be several add_header directives. These directives are inherited from the previous level if and only if there are no add_header directives defined on the current level.
http://nginx.org/en/docs/http/ngx_http_headers_module.html#add_header

@rxaviers

This comment has been minimized.

rxaviers commented Sep 18, 2016

@sgnl

This comment has been minimized.

sgnl commented Oct 30, 2016

Internet Archive for http://tautt.com/best-nginx-configuration-for-security
here: http://web.archive.org/web/20151011140918/http://tautt.com/best-nginx-configuration-for-security

@snoek09

This comment has been minimized.

snoek09 commented Dec 6, 2016

ECDHE-RSA-DES-CBC3-SHA, EDH-RSA-DES-CBC3-SHA and DES-CBC3-SHA are weak ciphers.

@tardis40

This comment has been minimized.

tardis40 commented Dec 26, 2016

why no HPKP? why no dual SSL boot? why no ECDSA certificate with ssl_ecdsa_curve? the set of suites are quite weak, I'm concerned about bulk block mod CBC here: ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA

here's what I have:

`# HTTPS server
server {
listen 443 ssl;

    server_name fortestingpurposesonly.xyz;
    ssl on;

    ssl_certificate      nginx_rsa.crt;
    ssl_certificate_key  server.key;

    ssl_certificate blablabla.crt;
    ssl_certificate_key ec.key;

    add_header Strict-Transport-Security max-age=63072000;
    add_header Public-Key-Pins 'pin-sha256=\"RyVHLvC/L9OizFGYr+ujdCAdQL2vpmbquWSVwagQOzE=\"; pin-sha256=\"EohwrK1N7rr3bRQphPj4j2cel+B2d0NNbM9PWHNDXpM=\"; max-age=25920; includeSubDomains';
    add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection "1; mode=block";

    ssl_session_cache    shared:SSL:1m;
    ssl_session_timeout  5m;
    ssl_ecdh_curve secp384r1:secp256k1;

    ssl_protocols TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:!aNULL:!eNULL:!LOW:!3DES:!MD5:!CBC; `

And here's OCSP stapling, you know that im sure:
`
#OCSP Stapling

ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /root/ssltest/ec.ca-bundle;
resolver 8.8.8.8 8.8.4.4; `

this is more like it, imo.
sorry my formatting

@petarov

This comment has been minimized.

petarov commented Jan 11, 2017

dhparam.pem file generation may take a decent amount of time, especially on RPis.

One solution is to use DSA rather than DH parameters, converted to DH format.

openssl dhparam -dsaparam -out /etc/nginx/ssl/dhparam.pem 2048
@grandcat

This comment has been minimized.

grandcat commented Jan 23, 2017

Regarding automatic redirect and IPv6:
Someone might want to add listen [::]:80 ipv6only=on; so Nginx correctly forwards its IPv6 visitors.

So, the complete redirect block would look like this:

# redirect all http traffic to https
server {
  listen 80;
  listen [::]:80 ipv6only=on;
  server_name .forgott.com;
  return 301 https://$host$request_uri;
}

Same applies to the other server block handling the real requests:

server {
  listen 443 ssl default deferred;
  listen [::]:443 ssl ipv6only=on;
  server_name .forgott.com;
  # ...
}
@C0nw0nk

This comment has been minimized.

C0nw0nk commented Mar 1, 2017

Why do you not enable http2

listen 443 ssl http2 default deferred; #IPv4
listen [::]:443 ssl http2 default deferred; #IPv6
@fedulovivan

This comment has been minimized.

fedulovivan commented Mar 7, 2017

+1 for users @mhcerri @gertvdijk @sander who already highlighted above an issue with overriding add_headers directive

@jspiro

This comment has been minimized.

jspiro commented Mar 15, 2017

Has anyone built a new version of this with the recommendations above?

@C0nw0nk

This comment has been minimized.

C0nw0nk commented Mar 23, 2017

OWASP recommendations are more up to date.

https://www.owasp.org/index.php/SCG_WS_nginx#SSL_Module

Also your resolver should be more like this.

resolver 8.8.8.8 8.8.4.4 valid=300s; #Cache resolver
@plentz

This comment has been minimized.

Owner

plentz commented Jul 2, 2017

@jeremysears thanks, I will update the protocols and cipher suites. It's been a while since the last time I've updated this gist

@kravietz

This comment has been minimized.

kravietz commented Jul 5, 2017

I believe https://cipherli.st/ is also quite an up to date resource on Nginx configuration.

@benscobie

This comment has been minimized.

benscobie commented Aug 12, 2017

Mozilla also provide an SSL config generator: https://mozilla.github.io/server-side-tls/ssl-config-generator/

@ks228

This comment has been minimized.

ks228 commented Aug 24, 2017

I have paid 99 dollars for this year on some expensive ssl from godaddy. I wish I knew this one earlier. Your tutorial along with let's crypt ssl will help me a lot now
https://infoginx.com/ig-securing-nginx-server-lets-encrypt-ubuntu/

@ghprod

This comment has been minimized.

ghprod commented Sep 4, 2017

This is great!

Thanks 👍

@stepan-i

This comment has been minimized.

stepan-i commented Sep 15, 2017

Your blogpost link is broken! :( I've added this GIST to https://github.com/wallarm/awesome-nginx-security

@heisGarvit

This comment has been minimized.

heisGarvit commented Oct 7, 2017

@MichaeMimouni try adding the -dsaparam option.

openssl dhparam -dsaparam -out /etc/ssl/private/dhparam.pem 4096

as answered here https://security.stackexchange.com/a/95184

@1985a

This comment has been minimized.

1985a commented Nov 3, 2017

Hi there. Thanks you for sharing this. I wish I found this before. I was able to get this by using a configuration like this one.
img

@jult

This comment has been minimized.

jult commented Nov 16, 2017

FYI, here's my include for TLS/SSL and nginx: https://gist.github.com/jult/395ad9fd3e9773a54a67aaf689beab27

@Rotzbua

This comment has been minimized.

Rotzbua commented Nov 23, 2017

There is a mistake: includeSubdomains should be includeSubDomains
The header should not be processed case sensitive but to match the RFC https://tools.ietf.org/html/rfc6797 it would be good to use that form.

@knoxcard

This comment has been minimized.

knoxcard commented Nov 29, 2017

Look at this: https://scotthelme.co.uk/a-new-security-header-referrer-policy/

add this your nginx.conf file ...

Referrer Policy

add_header Referrer-Policy "no-referrer";

Update nginx configuration: nginx -s reload

Then test here: https://securityheaders.io/

@katguzmann

This comment has been minimized.

katguzmann commented Feb 2, 2018

How does the CSP for Google Analytics work on a separate file, how should be look like?
I'm having a hard time trying to figure this out, since a lot of sources say just to add 'unsafe-inline' on the script-src just to support Google Analytics, but it seems to be very unsafe.

Should I just create an analytics.conf and add the following:

script-src 'self' https://www.google-analytics.com;
img-src https://www.google-analytics.com www.google-analytics.com https://stats.g.doubleclick.net;
connect-src https://www.google-analytics.com www.google-analytics.com https://stats.g.doubleclick.net

and then, on the nginx.conf:
default-src 'none';script-src 'self' www.google-analytics.com;img-src www.google-analytics.com;

Also, I'm very confused about how the config file for nginx should look like, I have the present reference and this one:

<FilesMatch "\.(html|php)$">
Header set Content-Security-Policy "policy-definition"
</FilesMatch>

I'm not a back-end developer, and this is the first time that I create a CSP, so I'll be very grateful for any help!

@aptalca

This comment has been minimized.

aptalca commented Feb 6, 2018

An nginx block only inherits add_header directives from the upper level if there are no add_header directives set in that block.

Once you add the add_header directive for the HSTS in the server block, it will ignore the four add_header directives you set in the http block above.

@colinmollenhour

This comment has been minimized.

colinmollenhour commented May 1, 2018

The ECDHE-RSA-CHACHA20-POLY1305 cipher is non-compliant with NIST and therefore HIPAA.

@AdrianLThomas

This comment has been minimized.

AdrianLThomas commented May 31, 2018

A referrer policy should be added in to here too. Without this header, you will still get flagged up on securityheaders.com

https://scotthelme.co.uk/a-new-security-header-referrer-policy/

@neilwashere

This comment has been minimized.

neilwashere commented Jun 6, 2018

Thanks for making this available. Very useful resource 👍

@GaProgMan

This comment has been minimized.

GaProgMan commented Jul 12, 2018

This is an absolutely amazing resource. Thank you for creating and sharing it 👍

@aidan-fitz

This comment has been minimized.

aidan-fitz commented Aug 14, 2018

I second what @mhcerri said. It's a gotcha.

@jukbot

This comment has been minimized.

jukbot commented Aug 17, 2018

Should add prime256v1 to ssl_ecdh_curve too, if you want to connect to app in Android <= 7

Ref: https://stackoverflow.com/questions/47232191/letsencrypt-does-not-work-on-android

@C0nw0nk

This comment has been minimized.

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

@egberts

This comment has been minimized.

egberts commented Oct 20, 2018

I like this portion better for 301 redirect from http to https, rather than a hardcoded URL value:

return 301 https://$host$request_uri;
@egberts

This comment has been minimized.

egberts commented Oct 20, 2018

@C0nw0nk, Here is a list of Open DNS servers that can prove that my highly hardened personal website is a secured and “un-hackable”. Basically this test was done using the “dig” tool with ad notation of whether DNSSEC is supported (and a small notation if ca is supported too)
The command used is:

dig +dnssec mywebsiteredacted.net. @# and 'ad' flag appears as authenticated,# and note any absense of 'ca' flag

DNSSEC? IP Provider Name
YES 185.228.168.9 CleanBrowsing DNS
YES 185.228.169.9 CleanBrowsing DNS
YES 156.154.70.1 DNS Advantage
YES 156.154.71.1 DNS Advantage
YES 84.200.69.80 DNS Watch
YES 84.200.70.40 DNS Watch
YES 216.146.35.35 Dyn
YES 216.146.36.36 Dyn
YES 81.218.119.11 GreenTeamDNS
YES 74.82.42.42 HurricaneElectric
YES 156.154.70.1 NeuStar
YES 156.154.71.1 NeuStar
YES 9.9.9.9 Quad9
YES 149.112.112.112 Quad9
If you are using any of the DNS servers below this line, then when you visit your banking website, your transaction will not be safe:
YES 199.85.126.10 Norton ConnectSafe (discontinued)
YES 199.85.126.20 Norton ConnectSafe (discontinued)
YES 199.85.127.10 Norton ConnectSafe (discontinued)
no 1.0.0.1 Cloudflare
no 109.69.8.51 puntCAT
no 1.1.1.1 Cloudflare
no 176.103.130.130 AdGuard
no 176.103.130.131 AdGuard
no 176.103.130.132 AdGuard Family
no 176.103.130.134 AdGuard Family
no 195.46.39.39 SafeDNS
no 195.46.39.40 SafeDNS
no 198.101.242.72 AlternateDNS
no 209.244.0.3 Level 3
no 209.244.0.4 Level 3
no 23.253.163.53 AlternateDNS
no 37.235.1.174 FreeDNS
no 37.235.1.177 FreeDNS
no 77.88.8.1 YandexDNS
no 77.88.8.8 YandexDNS
no 8.8.4.4 Google DNS
no 8.8.8.8 Google DNS
no 89.233.43.71 UncensoredDNS
no 91.239.100.100 UncensoredDNS
no bind.odvr.dns-oarc.net DNS OARC
no NOcd 208.67.220.123 OPENDNS FamilyShield
no NOcd 208.67.220.220 OPENDNS
no NOcd 208.67.222.123 OPENDNS FamilyShield
no NOcd 208.67.222.222 OPENDNS
no NOcd 8.20.247.20 Comodo
no NOcd 8.26.56.26 Comodo

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