Not sure it's a good thing to answer this probably "dead and buried" question but here we go...
Basic configuration (i.e. meh security)
- Generate your private key (>= 2048bit) and your certificate. I assume you have them already, otherwise have a look at letsencrypt.org. Make sure your certificate includes the full certificate chain (usually intermediate/entity certificates). Regarding Diffie-Hellman parameters, you can generate them by running
openssl dhparam -out /path/to/dhparam.pem 2048
.
Set your SSL settings in accordance to Mozilla SSL config generator (intermediate settings, as of 2016-06-30):
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name localhost;
root /var/www/wordpress;
### SSL/TLS SETTINGS ###
# certs sent to the client in SERVER HELLO are concatenated in ssl_certificate
ssl_certificate /path/to/signed_cert_plus_intermediates;
ssl_certificate_key /path/to/private_key;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
# Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
ssl_dhparam /path/to/dhparam.pem;
# intermediate configuration. tweak to your needs.
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
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';
ssl_prefer_server_ciphers on;
### OCSP Stapling ###
# fetch OCSP records from URL in ssl_certificate and cache them
ssl_stapling on;
ssl_stapling_verify on;
# verify chain of trust of OCSP response using Root CA and Intermediate certs
ssl_trusted_certificate /path/to/root_CA_cert_plus_intermediates;
resolver <IP DNS resolver>;
include /etc/nginx/default.d/*.conf;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
Advanced configuration (i.e. come at me, NSA ! (well no, but...))
Take the Basic configuration above and tweak it as follow:
- Select (at least) 3072bit when you generate your certificate and your Diffie-Hellman parameters (yes, it will take a while but it's worth it).
- Use TLS1.2 only:
ssl_protocols TLSv1.2;
- Use a safe curve:
ssl_ecdh_curve secp384r1;
- Use "modern" cipher suites (as of 2016-06-30):
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
Enforce HTTPS for everyone:
Rewrite your "HTTP" server
block as follow:
server {
listen 80;
listen [::]:80;
server_name mySuperServer;
return 301 https://$server_name$request_uri;
}
Use a HSTS header to force your visitor's navigator to use HTTPS exclusively: add_header Strict-Transport-Security 'max-age=31536000; includeSubdomains; preload';
You server can also protect your website (to some extend) by setting up those headers:
add_header X-Frame-Options DENY;
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options nosniff;
add_header Content-Security-Policy "default-src 'none';
- General best practice here: Never use
root
to modify your files. Use sudo
instead.
Bonus
- If your server is running nginx > 1.9.5, you can use HTTP/2 by
adding
http2
in your listen
directive.
- Your server got an IPv6 address (and supports HTTP/2) ? Good ! Add
listen [::]:443 ssl http2;
below the first listen
directive.
- Do you plan to use your server for several domains ? You should set the
access_log
and error_log
for each server
block then.
- Copy/pasting is rarely arousing... You can move the
### SSL/TLS SETTINGS ###
block in a text file and include it in your configuration. Same for your CGI call to PHP.
- You can speed up the communications between nginx and your PHP interpreter a bit by using a UNIX socket instead of listening to you loopback interface:
fastcgi_pass unix:/var/run/php5-fpm.sock;
.
As a result, your server
block should look something like this:
#HTTP server
server {
listen 80;
listen [::]:80;
server_name mySuperServer;
return 301 https://$server_name$request_uri;
}
# HTTPS server
server {
listen 443 ssl http2 default_server;
listen [::]:443 ssl http2;
server_name mySuperServer;
index index.php index.html index.htm;
root /path/to/your/files/mySuperServer;
access_log /var/log/nginx/mySuperServer/access.log;
error_log /var/log/nginx/mySuperServer/error.log;
### SSL/TLS SETTINGS ###
ssl on;
ssl_certificate /path/to/your/cert.pem;
ssl_certificate_key /path/to/your/privkey.pem;
ssl_dhparam /path/to/your/dh_parameters.pem;
include securityrules.inc;
include fastcgi.inc;
}
Content of securityrules.inc
:
ssl_protocols TLSv1.2;
ssl_ecdh_curve secp384r1;
ssl_prefer_server_ciphers on;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
### HTTP HEADERS ###
add_header Strict-Transport-Security 'max-age=31536000; includeSubdomains; preload';
add_header X-Frame-Options DENY;
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options nosniff;
add_header Content-Security-Policy "default-src 'none';
Content of fastcgi.inc
:
location ~ \.php$ {
include fastcgi_params;
fastcgi_keep_conn on;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
nginx
give? (check the log files) Also you might be missing a semicolon at the end of theindex
line in the https part.