nginx Config

servers Aug 30, 2020

This is my commonly used config for nginx. It may be out of date, so just use it as a more general guide.

First set up DH params

mkdir -p /etc/nginx/ssl
cd /etc/nginx/ssl
openssl dhparam -out dhparam.pem 2048

Main Config (nginx.conf)

user www-data;
worker_processes auto;
pid /run/nginx.pid;

events {
	worker_connections 768;
	# multi_accept on;
}

http {

	##
	# Basic Settings
	##

	sendfile on;
	tcp_nopush on;
	tcp_nodelay on;
	keepalive_timeout 65;
	types_hash_max_size 2048;
	server_tokens off;

	server_names_hash_bucket_size 64;
	server_names_hash_max_size 512;
	# server_name_in_redirect off;

	client_max_body_size 128M;

	include /etc/nginx/mime.types;
	default_type application/octet-stream;

	##
	# SSL Settings
	##

	ssl_protocols TLSv1.3 TLSv1.2;
	ssl_prefer_server_ciphers on;
	ssl_ciphers "EECDH+AESGCM:EDH+AESGCM";
	ssl_ecdh_curve secp384r1;
	ssl_dhparam /etc/nginx/ssl/dhparam.pem;
	ssl_session_cache shared:SSL:50m;
	ssl_session_timeout 5m;
	ssl_session_tickets off;
	ssl_stapling on;
	ssl_stapling_verify on;
	# every HTTPS host needs to configure its `ssl_trusted_certificate`	

	##
	# Logging Settings
	##

	error_log /var/log/nginx/error.log;
	access_log off;

	##
	# Gzip Settings
	##

	gzip on;
	gzip_comp_level 2;
	gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml;

	##
	# Application Security
	##
	
	include snippets/security.conf;

	##
	# Virtual Host Configs
	##

	# default config
	server {
		listen 80 default_server;
		listen [::]:80 default_server;
		server_name _;
		log_not_found off;

		# either show splash screen
		# index index.html;
		# root /var/www/__default;

		# or redirect to server main URL
		# return 303 https://main-domain.com;
	}

	include /etc/nginx/conf.d/*.conf;
	include /etc/nginx/servers/*;
}

PHP-FPM Snippet (snippets/php-fpm.conf)

fastcgi_buffer_size             128k;
fastcgi_buffers                 4 256k;
fastcgi_busy_buffers_size       256k;
fastcgi_temp_file_write_size    256k;
fastcgi_intercept_errors        on;

# Symfony-specific config
fastcgi_param APP_ENV "prod";

# prevent httpoxy vulnerability
fastcgi_param HTTP_PROXY "";

include snippets/fastcgi-php.conf;

Security-Snippet (snippets/security.conf)

Warning: you need to include this file everywhere, where you want to load additional headers via add_header. See for example the /assets/ location in the file below.

add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

Project Config (servers/example.org.conf)

# redirect HTTP to HTTPS
server {
    listen 80;
    listen [::]:80;
    server_name example.org www.example.org;
    
    return 301 https://www.example.org$request_uri;
}

# redirect non-WWW to WWW
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    server_name example.org;

    ssl_certificate         /etc/letsencrypt/live/example.org/fullchain.pem;
    ssl_certificate_key     /etc/letsencrypt/live/example.org/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/example.org/chain.pem;
    
    return 301 https://www.example.org$request_uri;
}

# actual server block
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    server_name www.example.org;
    root /var/www/example.org/public;

    ssl_certificate         /etc/letsencrypt/live/example.org/fullchain.pem;
    ssl_certificate_key     /etc/letsencrypt/live/example.org/privkey.pem;    
    ssl_trusted_certificate /etc/letsencrypt/live/example.org/chain.pem;

    error_log /var/log/nginx/example.org-error.log;

    location / {
        index index.php;
        try_files $uri /index.php$is_args$args;
    }

    location ~ ^/index.php {
        include snippets/php-fpm.conf;
        fastcgi_pass 127.0.0.1:9100;
        internal;
    }

    location ~ ^/assets/ {
        include snippets/security.conf;
        add_header Cache-Control "public, max-age=31536000, immutable";
        add_header Vary "Accept-Encoding";
        log_not_found off;
    }

    location ~ \.php$ {
        return 404;
    }

    location ~ /\. {
        deny all;
        log_not_found off;
    }
}

Additional Config / Comments

Additional Headers

You can set additional headers

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

# 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=63072000; includeSubDomains; preload";

Cipherlist

Use the cipherlist from https://cipherli.st/

OCSP stapling with DigiCert

OCSP stapling with the root certificate from DigiCert: https://www.digitalocean.com/community/tutorials/how-to-configure-ocsp-stapling-on-apache-and-nginx

wget -O - https://www.digicert.com/CACerts/DigiCertHighAssuranceEVRootCA.crt | openssl x509 -inform DER -outform PEM | tee -a ca-certs.pem> /dev/null
wget -O - https://www.digicert.com/CACerts/DigiCertHighAssuranceEVCA-1.crt | openssl x509 -inform DER -outform PEM | tee -a ca-certs.pem> /dev/null

Photo credit: Pablo Torrado

Tags

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.