# 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.2 TLSv1.3; | |
# 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 | |
} |
This comment has been minimized.
This comment has been minimized.
@ryankearney updated, thanks :) |
This comment has been minimized.
This comment has been minimized.
ssl_session_timeout 5m; <- Should this appear twice in the config? |
This comment has been minimized.
This comment has been minimized.
@barrybingo that is probably a typo. once is just enough. |
This comment has been minimized.
This comment has been minimized.
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; |
This comment has been minimized.
This comment has been minimized.
How to force redirect all request to www. version? server { server { domain.com to https://www.domain.com = Works |
This comment has been minimized.
This comment has been minimized.
@barrybingo not really, thanks! |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
nice work |
This comment has been minimized.
This comment has been minimized.
RoldanLT ssltunnel.net to https://www.ssltunnel.net = Works
|
This comment has been minimized.
This comment has been minimized.
Thanks vincentclee! server { server {
} server { |
This comment has been minimized.
This comment has been minimized.
@vincentclee @RoldanLT Beware of www redirect : |
This comment has been minimized.
This comment has been minimized.
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:
http://trac.nginx.org/nginx/ticket/289 Most examples use the following to implement HSTS, which does not follow the RFC.
|
This comment has been minimized.
This comment has been minimized.
@toddlahman : Shouldn't we avoid to set the header for http (port 80) ?
|
This comment has been minimized.
This comment has been minimized.
In nginx >= 1.7.5 you should have (note the added "always"):
This forces the header to always be added regardless of the http code which is what is wanted for this type of header. |
This comment has been minimized.
This comment has been minimized.
I'm a bit curious, you seem to use
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 |
This comment has been minimized.
This comment has been minimized.
Use following ciphers to exclude RC4.
|
This comment has been minimized.
This comment has been minimized.
i am getting access denied error |
This comment has been minimized.
This comment has been minimized.
@chrisnew thanks for the RC4-less ciphers. |
This comment has been minimized.
This comment has been minimized.
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 |
This comment has been minimized.
This comment has been minimized.
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. |
This comment has been minimized.
This comment has been minimized.
EDH+aRSA is now considered weak http://imgur.com/C5fKr38 I updated my ciphers string to:
|
This comment has been minimized.
This comment has been minimized.
maybe you could add |
This comment has been minimized.
This comment has been minimized.
@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 |
This comment has been minimized.
This comment has been minimized.
Why are the |
This comment has been minimized.
This comment has been minimized.
@oliversalzburg: +1 for google analytic url. |
This comment has been minimized.
This comment has been minimized.
|
This comment has been minimized.
This comment has been minimized.
More HSTS updates: new requirements from Chromium team on getting in the preload list of browsers (and a core point of HSTS) |
This comment has been minimized.
This comment has been minimized.
Having TLSv1 enabled fails the latest trustwave.com PCI scan btw. |
This comment has been minimized.
This comment has been minimized.
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
Response:
|
This comment has been minimized.
This comment has been minimized.
This param Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bitsssl_dhparam /etc/nginx/ssl/dhparam.pem; |
This comment has been minimized.
This comment has been minimized.
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 |
This comment has been minimized.
This comment has been minimized.
As indicated by @mhcerri, the Please see my blog post for a further explanation: Nginx add_header configuration pitfall. |
This comment has been minimized.
This comment has been minimized.
This configuration uses very weak protocols and cipher suites. I recommend using this generator to get the latest recommended NGINX configs: |
This comment has been minimized.
This comment has been minimized.
Thanks in a lot! |
This comment has been minimized.
This comment has been minimized.
@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? |
This comment has been minimized.
This comment has been minimized.
@conradwt I'm experiencing the same issues. When either doing a test either with
|
This comment has been minimized.
This comment has been minimized.
http://tautt.com/best-nginx-configuration-for-security/ does no longer exist :< |
This comment has been minimized.
This comment has been minimized.
You should also listen on IPv6...
|
This comment has been minimized.
This comment has been minimized.
How should this config change to protect against https://blog.cloudflare.com/yet-another-padding-oracle-in-openssl-cbc-ciphersuites/ ? |
This comment has been minimized.
This comment has been minimized.
@fotonobile actually you need to update your OpenSSL to fix that (I had to restart my server as well) |
This comment has been minimized.
This comment has been minimized.
Just included those values inside my nginx 1.11 config. Strange. |
This comment has been minimized.
This comment has been minimized.
@morsik did you have additional
|
This comment has been minimized.
This comment has been minimized.
Upvoting an answer above. Use https://mozilla.github.io/server-side-tls/ssl-config-generator/. |
This comment has been minimized.
This comment has been minimized.
Internet Archive for |
This comment has been minimized.
This comment has been minimized.
|
This comment has been minimized.
This comment has been minimized.
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: here's what I have: `# HTTPS server
And here's OCSP stapling, you know that im sure: ssl_stapling on; this is more like it, imo. |
This comment has been minimized.
This comment has been minimized.
One solution is to use DSA rather than DH parameters, converted to DH format.
|
This comment has been minimized.
This comment has been minimized.
Regarding automatic redirect and IPv6: 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;
# ...
} |
This comment has been minimized.
This comment has been minimized.
Why do you not enable
|
This comment has been minimized.
This comment has been minimized.
+1 for users @mhcerri @gertvdijk @sander who already highlighted above an issue with overriding add_headers directive |
This comment has been minimized.
This comment has been minimized.
Has anyone built a new version of this with the recommendations above? |
This comment has been minimized.
This comment has been minimized.
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.
|
This comment has been minimized.
This comment has been minimized.
@jeremysears thanks, I will update the protocols and cipher suites. It's been a while since the last time I've updated this gist |
This comment has been minimized.
This comment has been minimized.
I believe https://cipherli.st/ is also quite an up to date resource on Nginx configuration. |
This comment has been minimized.
This comment has been minimized.
Mozilla also provide an SSL config generator: https://mozilla.github.io/server-side-tls/ssl-config-generator/ |
This comment has been minimized.
This comment has been minimized.
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 |
This comment has been minimized.
This comment has been minimized.
This is great! Thanks |
This comment has been minimized.
This comment has been minimized.
Your blogpost link is broken! :( I've added this GIST to https://github.com/wallarm/awesome-nginx-security |
This comment has been minimized.
This comment has been minimized.
@MichaeMimouni try adding the
as answered here https://security.stackexchange.com/a/95184 |
This comment has been minimized.
This comment has been minimized.
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. |
This comment has been minimized.
This comment has been minimized.
FYI, here's my include for TLS/SSL and nginx: https://gist.github.com/jult/395ad9fd3e9773a54a67aaf689beab27 |
This comment has been minimized.
This comment has been minimized.
There is a mistake: |
This comment has been minimized.
This comment has been minimized.
Look at this: https://scotthelme.co.uk/a-new-security-header-referrer-policy/ add this your nginx.conf file ... Referrer Policyadd_header Referrer-Policy "no-referrer"; Update nginx configuration: nginx -s reload Then test here: https://securityheaders.io/ |
This comment has been minimized.
This comment has been minimized.
How does the CSP for Google Analytics work on a separate file, how should be look like? Should I just create an
and then, on the nginx.conf: Also, I'm very confused about how the config file for nginx should look like, I have the present reference and this one:
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! |
This comment has been minimized.
This comment has been minimized.
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. |
This comment has been minimized.
This comment has been minimized.
The |
This comment has been minimized.
This comment has been minimized.
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/ |
This comment has been minimized.
This comment has been minimized.
Thanks for making this available. Very useful resource |
This comment has been minimized.
This comment has been minimized.
This is an absolutely amazing resource. Thank you for creating and sharing it |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
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 |
This comment has been minimized.
This comment has been minimized.
Should use a privacy respecting and worlds fastest DNS resolver instead of Google's Here is my code for their resolver to support both IPv4 and IPv6 (Can disable IPv6 and will still work universal setup)
https://1.1.1.1/#explanation Fastest DNS resolvers in the world (1.1.1.1 is the fastest most secured and private unlike google) |
This comment has been minimized.
This comment has been minimized.
I like this portion better for 301 redirect from http to https, rather than a hardcoded URL value:
|
This comment has been minimized.
This comment has been minimized.
@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) dig +dnssec mywebsiteredacted.net. @# and 'ad' flag appears as authenticated,# and note any absense of 'ca' flag DNSSEC? IP Provider Name |
This comment has been minimized.
This comment has been minimized.
For letsencrypt:
Edit: changed |
This comment has been minimized.
This comment has been minimized.
Thanks for the config. I try to implement using nginx 1.16.0 stable. What is wrong with my config (etc/nginx/conf.d/default.conf) file? I am keep getting nginx default 404. I can not override error 400 and 500 to points the my index.html file. Any idea how to correct the issue.
|
This comment has been minimized.
This comment has been minimized.
The To avoid such pitfalls, use ngx_security_headers module which knows when to set respective header and also accounts for things like not sending headers during conditional
|
This comment has been minimized.
This comment has been minimized.
Hi @plentz, |
This comment has been minimized.
This comment has been minimized.
I compiled few optimization hacks to increase requests/second. |
This comment has been minimized.
This comment has been minimized.
I see you currently have |
This comment has been minimized.
This comment has been minimized.
TLSv1.3 should be added: |
This comment has been minimized.
This comment has been minimized.
TLSv1 & TLSv1.1 should be removed. Otherwise the HTTPS rating is capped at https://blog.qualys.com/ssllabs/2018/11/19/grade-change-for-tls-1-0-and-tls-1-1-protocols |
This comment has been minimized.
This comment has been minimized.
@rooch84 thanks, I've updated it. |
This comment has been minimized.
This comment has been minimized.
These are my settings for nginx with LetsEncrypt SSL for an A+ score on Qualys: ssl_session_cache shared:le_nginx_SSL:1m; ssl_dhparam /etc/nginx/ssl/dhparam.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; resolver 1.1.1.1 1.0.0.1 [2606:4700:4700::1111] [2606:4700:4700::1001]; add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"; For dhparam.pem I've used this: I would suggest installing haveged and have it running prior of dhparam.pem generation (otherwise it would take quite some long time) |
This comment has been minimized.
This comment has been minimized.
Thanks for this! Very useful indeed! |
This comment has been minimized.
This comment has been minimized.
Here is HTTP/3 support server {
listen 443 ssl; # TCP listener for HTTP/1.1
listen 443 http3 reuseport; # UDP listener for QUIC+HTTP/3
ssl_protocols TLSv1.3; # QUIC requires TLS 1.3
ssl_certificate ssl/www.example.com.crt;
ssl_certificate_key ssl/www.example.com.key;
add_header Alt-Svc 'quic=":443"'; # Advertise that QUIC is available
add_header QUIC-Status $quic; # Sent when QUIC was used
} |
This comment has been minimized.
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