Securing django with Let's Encrypt

django https security ssl

I just finished to setup ssl encryption for abidibo.net, using Let's Encrypt Certificate Authority. This post serves as a reminder for me, and maybe a sort of guide for you.

This post assumes the website runs with the following technologies:

  • django >= 1.8
  • nginx
  • debian 8 (jessie) with ssh access

Why Let's Encrypt?

Let's Encrypt is a free, automated, and open certificate authority (CA), run for the public’s benefit. It is a service provided by the Internet Security Research Group (ISRG).

So, the interesting part is: it's free, and provides ssl certificates. That's it!

Some extracts from Let's Encrypt Getting Started guide:

In order to get a certificate for your website’s domain from Let’s Encrypt, you have to demonstrate control over the domain. With Let’s Encrypt, you do this using software that uses the ACME protocol, which typically runs on your web host.

I assume you have ssh access to your debian jessie powered machine, in that case:

We recommend that most people with shell access use the Certbot ACME client. It can automate certificate issuance and installation with no downtime. It also has expert modes for people who don’t want autoconfiguration. It’s easy to use, works on many operating systems, and has great documentation.

Let's go with Certbot...

Certbot, how to

Certbot is an easy-to-use automatic client that fetches and deploys SSL/TLS certificates for your webserver. Certbot was developed by EFF and others as a client for Let’s Encrypt and was previously known as “the official Let’s Encrypt client” or “the Let’s Encrypt Python client.”

In order to install Certbot you need to enable the Jessie backports repo, so edit your /etc/apt/sources.list and add this line:

deb http://ftp.debian.org/debian jessie-backports main

Then perform an update:

$ sudo apt-get update

And you're ready to install Certbot:

$ sudo apt-get install certbot -t jessie-backports

Ok. In my case that wasn't enough, because I had an old pip version and an old request package version, so I received an error like this one:

AttributeError: 'module' object has no attribute 'PROTOCOL_SSLv3'

I solved it running:

$ sudo easy_install --upgrade pip
$ pip install --upgrade requests

Now you should have Certbot properly installed in your system. Let's create the certificate!
The Certbot client supports a number of different “plugins” that can be used to obtain and/or install certificates, I'll use the webroot plugin, since it doesn't require to stop the web server.

To use the webroot plugin, your server must be configured to serve files from hidden directories. If /.well-known is treated specially by your webserver configuration, you might need to modify the configuration to ensure that files inside /.well-known/acme-challenge are served by the webserver.

What does it mean?

Before running the command which actually creates the certificate, you need to ensure that the web server can serve static files under the web root at the following path: /.well-know. Since all paths intrcepted by the django application are served through wsgi, you need to write a nginx configuration role in order to serve statically such resources: add the following to your domain nginx configuration file:

location /.well-known {
  root /your/path;

Where /your/path is a directory which will be used when launching the Cerbot command. I decided to use the same directory which contains the folder static, where the django static files are collected.

Reload nginx and you're ready to create the certificate:

$ certbot certonly --webroot -w /your/path -d example.com -d www.example.com

This command will obtain a single cert for example.com and www.example.com; it will place files below /your/path to prove control of the two domains.

You'll be asked to provide an e-mail address used for urgent notices and lost key recovery, and to accept the Let's Encrypt Subscribe Agreement. Easy!

You'll receive an output like this:

 - If you lose your account credentials, you can recover through
   e-mails sent to sammy@digitalocean.com
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/example.com/fullchain.pem. Your
   cert will expire on 2017-01-07. To obtain a new version of the
   certificate in the future, simply run Let's Encrypt again.
 - Your account credentials have been saved in your Let's Encrypt
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Let's
   Encrypt so making regular backups of this folder is ideal.
 - If like Let's Encrypt, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

The certificate has been created and installed properly, now it's time to configure nginx and django to run over ssl, let's go!


Here you can find the official documentation:


In a nutshell, add these lines to your production configuration:


For applications running django<1.8 you should install and configure django-secure.

N.B. Make sure to load all external resources through https in your templates (js, fonts, css, images...), otherwise you'll get mixed content warnings.


There's only one thing left: configure the nginx server block in order to serve the contents through HTTPS. I post my entire virtual host configuration and we'll discuss the ssl related lines.

server {
    listen       80;
    server_name  abidibo.net www.abidibo.net;
    return       301 https://www.abidibo.net$request_uri;

server {
  listen 443 default ssl;

  ssl_certificate /etc/letsencrypt/live/abidibo.net/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/abidibo.net/privkey.pem;

  client_max_body_size 5M;
  server_name www.abidibo.net;
  access_log /var/log/nginx/access.log;
  error_log /var/log/nginx/error.log;

  if (-f /home/abidibo/www/abidibo-net/releases/current/.maintenance) {
    return 503;

  if (!-d /home/abidibo/www/abidibo-net/releases/current) {
    return 503;

  error_page 503 @maintenance;
  location @maintenance {
    root    /home/abidibo/www/abidibo-net/htdocs;
    rewrite ^(.*)$ /maintenance.html break;

  location /.well-known {
    root /home/abidibo/www/abidibo-net;
location /static {
    root /home/abidibo/www/abidibo-net;
  location /media {
    root /home/abidibo/www/abidibo-net;
  location /robots.txt {
    root /home/abidibo/www/abidibo-net/htdocs/robots.txt;
  location /sitemap.xml {
    root /home/abidibo/www/abidibo-net/htdocs/sitemap.xml.txt;
  location / {
    uwsgi_pass unix:/tmp/uwsgi_abidibo-net.sock;
    include /etc/nginx/uwsgi_params;

Lines 1-5
all no-HTTP traffic is redirected to HTTPS

Line 8
listen on the 443 port

Lines 10-11
paths to the certicate and key (replace abidibo.net with your domain)

Lines 32-34
these ones where used during the creation of the certificate

#tadaaa it's done!

Reload nginx, deploy your django app and you should have your new, brilliant https site.

Subscribe to abidibo.net!

If you want to stay up to date with new contents published on this blog, then just enter your email address, and you will receive blog updates! You can set you preferences and decide to receive emails only when articles are posted regarding a precise topic.

I promise, you'll never receive spam or advertising of any kind from this subscription, just content updates.

Subscribe to this blog

Comments are welcome!

blog comments powered by Disqus

Your Smartwatch Loves Tasker!

Your Smartwatch Loves Tasker!

Now available for purchase!