Yes, you can have nginx proxy requests to HTTP servers, and then itself respond to clients over HTTPS. When doing this, you will want to be sure that the nginx<->proxy connect is unlikely to be sniffed by whoever is your expected attacker. Safe-enough approaches might include:
- proxying to the same host (as you do)
- proxying to other hosts behind your firewall
Proxying to another host on the public Internet is unlikely to be safe-enough.
Here are instructions for obtaining a Let's Encrypt certificate using the same webserver you are using as a proxy.
Requesting your initial certificate from Let's Encrypt
Modify your server
clause to allow the subdirectory .well-known
to be served from a local directory, eg:
server {
listen 80;
server_name sub.domain.com www.sub.domain.com;
[…]
location /.well-known {
alias /var/www/sub.domain.com/.well-known;
}
location / {
# proxy commands go here
[…]
}
}
http://sub.domain.com/.well-known
is where the Let's Encrypt servers will look for the answers to the challenges it issues.
You can then use the certbot client to request a certificate from Let's Encrypt using the webroot plugin (as root):
certbot certonly --webroot -w /var/www/sub.domain.com/ -d sub.domain.com -d www.sub.domain.com
Your key, certificate, and certificate chain will now be installed in /etc/letsencrypt/live/sub.domain.com/
Configuring nginx to use your certificate
First create a new server clause like this:
server {
listen 443 ssl;
# if you wish, you can use the below line for listen instead
# which enables HTTP/2
# requires nginx version >= 1.9.5
# listen 443 ssl http2;
server_name sub.domain.com www.sub.domain.com;
ssl_certificate /etc/letsencrypt/live/sub.domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/sub.domain.com/privkey.pem;
# Turn on OCSP stapling as recommended at
# https://community.letsencrypt.org/t/integration-guide/13123
# requires nginx version >= 1.3.7
ssl_stapling on;
ssl_stapling_verify on;
# Uncomment this line only after testing in browsers,
# as it commits you to continuing to serve your site over HTTPS
# in future
# add_header Strict-Transport-Security "max-age=31536000";
access_log /var/log/nginx/sub.log combined;
# maintain the .well-known directory alias for renewals
location /.well-known {
alias /var/www/sub.domain.com/.well-known;
}
location / {
# proxy commands go here as in your port 80 configuration
[…]
}
}
Reload nginx:
service nginx reload
Verify that HTTPS now works by visiting https://sub.domain.com
and https://www.sub.domain.com
in your browser (and any other browsers you specifically wish to support) and checking that they don't report certificate errors.
Recommended: also review raymii.org: Strong SSL Security on nginx
and test your configuration at SSL Labs.
(Recommended) Redirect HTTP requests to HTTPS
Once you have confirmed that your site works with the https://
version of the URL, rather than have some users served insecure content because they went to http://sub.domain.com
, redirect them to the HTTPS version of the site.
Replace your entire port 80 server
clause with:
server {
listen 80;
server_name sub.domain.com www.sub.domain.com;
rewrite ^ https://$host$request_uri? permanent;
}
You should also now uncomment this line in the port 443 configuration, so that browsers remember to not even try the HTTP version of the site:
add_header Strict-Transport-Security "max-age=31536000";
Automatically renew your certificate
You can use this command (as root) to renew all certificates known to certbot and reload nginx using the new certificate (which will have the same path as your existing certificate):
certbot renew --renew-hook "service nginx reload"
certbot will only attempt to renew certificates that are more than 60 days old, so it is safe (and recommended!) to run this command very regularly, and automatically if at all possible. Eg, you could put the following command in /etc/crontab
:
# at 4:47am/pm, renew all Let's Encrypt certificates over 60 days old
47 4,16 * * * root certbot renew --quiet --renew-hook "service nginx reload"
You can test renewals with either a dry-run, which will contact Let's Encrypt staging servers to do a real test of contacting your domain, but won't store the resulting certificates:
certbot --dry-run renew
Or you can force an early renewal with:
certbot renew --force-renew --renew-hook "service nginx reload"
Note: you can dry run as many times as you like, but real renewals are subject to Let's Encrypt rate limits.