Let's say you want to host domains first.com
and second.com
.
Create folders for their files:
mkdir /var/www/first
mkdir /var/www/second
Create a text file /etc/nginx/sites-available/first.conf
containing:
server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
server_name first.com www.first.com;
root /var/www/first;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
Create a text file /etc/nginx/sites-available/second.conf
containing:
server {
listen 80;
listen [::]:80;
server_name second.com www.second.com;
root /var/www/second;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
Note that only the first domain has the keywords default_server
and ipv6only=on
in the listen
lines.
Replace the default virtual host:
sudo rm /etc/nginx/sites-enabled/default
sudo ln -s /etc/nginx/sites-available/first.conf /etc/nginx/sites-enabled/first.conf
sudo ln -s /etc/nginx/sites-available/second.conf /etc/nginx/sites-enabled/second.conf
sudo nginx -t
sudo systemctl stop nginx
sudo systemctl start nginx
Check that Nginx is running:
sudo systemctl status nginx
Expected results at this stage:
http://first.com
andhttp://www.first.com
serve the files from/var/www/first
http://second.com
andhttp://www.second.com
serve the files from/var/www/second
https://www.first.com
andhttps://www.second.com
don't work yet
Install Certbot for Nginx:
sudo apt-get update
sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update
sudo apt-get install -y python-certbot-nginx
Setup the certificates & convert Virtual Hosts to HTTPS:
sudo certbot --nginx
It will ask for:
- an email address
- agreeing to its Terms of Service
- which domains to use HTTPS for (it detects the list using
server_name
lines in your Nginx config) - whether to redirect HTTP to HTTPS (recommended) or not
You could stop here if all you want is HTTPS as this already gives you an A
rating and maintains itself.
Test your site with SSL Labs using https://www.ssllabs.com/ssltest/analyze.html?d=www.YOUR-DOMAIN.com
Expected results at this stage:
http://first.com
redirects tohttps://first.com
http://second.com
redirects tohttps://second.com
http://www.first.com
redirects tohttps://www.first.com
http://www.second.com
redirects tohttps://www.second.com
https://first.com
andhttps://www.first.com
serve the files from/var/www/first
https://second.com
andhttps://www.first.com
serve the files from/var/www/second
There is nothing to do, Certbot installed a cron task to automatically renew certificates about to expire.
You can check renewal works using:
sudo certbot renew --dry-run
You can also check what certificates exist using:
sudo certbot certificates
first.conf
should now look something like this, now that Certbot edited it:
server {
server_name first.com www.first.com;
root /var/www/first.com;
index index.html;
location / {
try_files $uri $uri/ =404;
}
listen [::]:443 ssl ipv6only=on; # managed by Certbot
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/first.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/first.com/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
if ($host = www.first.com) {
return 301 https://$host$request_uri;
} # managed by Certbot
if ($host = first.com) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80 default_server;
listen [::]:80 default_server;
server_name first.com www.first.com;
return 404; # managed by Certbot
}
Certbot didn't add HTTP/2 support when it created the new server blocks, so replace these lines:
listen [::]:443 ssl ipv6only=on;
listen 443 ssl;
by this:
listen [::]:443 ssl http2 ipv6only=on;
listen 443 ssl http2;
gzip off;
There is already an open Github issue
requesting Certbot to add http2
automatically, so hopefully this step can soon be removed.
The HTTPS server
blocks in first.conf
and second.conf
contain these lines, added by Certbot:
ssl_certificate /etc/letsencrypt/live/YOUR-DOMAIN/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/YOUR-DOMAIN/privkey.pem;
The stronger settings use OCSP Stapling, so each virtual host will need a ssl_trusted_certificate
as well.
Add this line (using the folder name that Certbot generated for your domain) after the ssl_certificate_key
line:
ssl_trusted_certificate /etc/letsencrypt/live/YOUR-DOMAIN/chain.pem;
Now let's edit the shared SSL settings at /etc/letsencrypt/options-ssl-nginx.conf
.
It most likely looks like this initially:
ssl_session_cache shared:le_nginx_SSL:1m;
ssl_session_timeout 1440m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
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";
If you tested with SSL Labs, you probably noticed that quite a few ciphers were flagged as "weak".
So replace the contents of the file with:
ssl_session_cache shared:le_nginx_SSL:1m;
ssl_session_timeout 1d;
ssl_session_tickets off;
ssl_protocols TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
ssl_ecdh_curve secp384r1;
ssl_stapling on;
ssl_stapling_verify on;
add_header Strict-Transport-Security "max-age=15768000; includeSubdomains; preload;";
add_header Content-Security-Policy "default-src 'none'; frame-ancestors 'none'; script-src 'self'; img-src 'self'; style-src 'self'; base-uri 'self'; form-action 'self';";
add_header Referrer-Policy "no-referrer, strict-origin-when-cross-origin";
add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
Now restart Nginx, and test the domain again with SSL Labs
using https://www.ssllabs.com/ssltest/analyze.html?d=www.YOUR-DOMAIN.com&latest
:
it should now be rated A+
, congratulations! 🙂
You could further improve using content-specific features like Content Security Policy
and Subresource Integrity
, and Brotli compression to replace gzip.
Online testing tools:
Useful links:
If Let's Encrypt is useful to you, consider donating to Let's Encrypt or donating to the EFF.