Skip to content

@plentz /nginx.conf
Last active

Embed URL

" readonly="readonly" aria-label="Gist Embed URL">

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
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'";
server {
listen 443 ssl default deferred;
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 5m;
# 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-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA: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:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
# 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;
ssl_stapling 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
add_header Strict-Transport-Security "max-age=31536000; includeSubdomains;";
# ... the rest of your configuration
}
# redirect all http traffic to https
server {
listen 80;
server_name .forgott.com;
return 301 https://$host$request_uri;
}
@ryankearney

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
Owner

@ryankearney updated, thanks :)

@barrybingo

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

@tzuryby

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

@altonius

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

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
Owner

@barrybingo not really, thanks!

@johnantoni

nice work

@vincentclee

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

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

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;";

@brunoqc

@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

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

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

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

i am getting access denied error

@paskal

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

@chrisnew thanks for the RC4-less ciphers.

@doriancosentino

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

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

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

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

@tierra

@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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.