Let’s Encrypt on a FreeBSD NGINX reverse proxy

This is a write-up on how I set up “Let’s Encrypt” on the reverse proxy sitting in front of the various VM’s serving a few of my websites. I looked at a guide which was very helpful, but I had to fill in on some gaps and tweak the configuration slightly. I’ll be outlining every step of the way here.

Let’s Encrypt let people enable HTTPS with a trusted certificate, for free. You can even get multiple-domain certificates, in case you run multiple websites behind a single IP address.

First of all, I installed the Let’s Encrypt package.

I then configured nginx to serve the magic folder for verification (/usr/local/etc/nginx/sites-enabled/letsencrypt.conf), and made my “real” vhosts only listen for SSL traffic. (You may have to temporarily disable them or add the magic stuff to each of them, if you didn’t have a SSL configuration at all before this.) I then reloaded nginx.

This is the catch-all ‘magic’ vhost for verification. It will redirect real traffic to the https version of the site.

server {
  server_name example.com something.example.com anotherdomain.example;
  listen 80;
  location '/.well-known/acme-challenge' {
    default_type "text/plain";
    root /tmp/letsencrypt-auto;
  location / {
    return 301 https://$host$request_uri;

I then executed the following commands to create my certificate:
export DOMAINS="-d example.com -d something.example.com -d anotherdomain.example"
export DIR=/tmp/letsencrypt-auto
mkdir -p $DIR
letsencrypt certonly --server https://acme-v01.api.letsencrypt.org/directory \
-a webroot --webroot-path=$DIR --agree-dev-preview $DOMAINS

This command outputs the path to the directory containing the certificate files. “privkey.pem” is the private key, and “fullchain.pem” is the file you want to use as certificate.

I updated the nginx configuration to use these certificates, in /usr/local/etc/nginx/nginx.conf:

http {
  ssl_certificate /usr/local/etc/letsencrypt/live/example.com/fullchain.pem;
  ssl_certificate_key /usr/local/etc/letsencrypt/live/example.com/privkey.pem;

I then created a script [letsencrypt_renew.sh] which renews the certificate when it’s 14 days or less from expiring. I set up crontab to call it once a day, and only report about any errornous output:

13 2 * * * /root/letsencrypt_renew.sh /usr/local/etc/letsencrypt/live/example.com/fullchain.pem > /dev/null

And that’s it. My websites which are hosted at home are now served over HTTPS with a trusted certificate. For free.

The Let’s Encrypt public beta will start 3rd December 2015, good luck!

7 thoughts on “Let’s Encrypt on a FreeBSD NGINX reverse proxy

  1. While using the pkg you have run into this:

    Traceback (most recent call last):
    File “/usr/local/bin/letsencrypt”, line 9, in
    load_entry_point(‘letsencrypt==0.0.0.dev20151123’, ‘console_scripts’, ‘letsencrypt’)()
    File “/usr/local/lib/python2.7/site-packages/letsencrypt/cli.py”, line 1206, in main
    return args.func(args, config, plugins)
    File “/usr/local/lib/python2.7/site-packages/letsencrypt/cli.py”, line 489, in obtain_cert
    le_client = _init_le_client(args, config, authenticator, installer)
    File “/usr/local/lib/python2.7/site-packages/letsencrypt/cli.py”, line 171, in _init_le_client
    acc, acme = _determine_account(args, config)
    File “/usr/local/lib/python2.7/site-packages/letsencrypt/cli.py”, line 158, in _determine_account
    config, account_storage, tos_cb=_tos_cb)
    File “/usr/local/lib/python2.7/site-packages/letsencrypt/client.py”, line 115, in register
    File “/usr/local/lib/python2.7/site-packages/cryptography/hazmat/primitives/asymmetric/rsa.py”, line 103, in generate_private_key
    return backend.generate_rsa_private_key(public_exponent, key_size)
    File “/usr/local/lib/python2.7/site-packages/cryptography/hazmat/backends/multibackend.py”, line 129, in generate_rsa_private_key

    I’ll try pip now but seems that RSA support is busted in py27-cryptography from ports


  2. Hello,
    Thanks for your “letsencrypt_renew.sh” script.
    Since some time (days, weeks?), it looks like they’ve renamed “–renew” as “–renew-by-default”: I had to change it in your script or else it would fail to renew the certificate.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s