Let's Encrypt certificate renewal with nginx

Recently I struggled setting up Let’s Encrypt certificate renewal with nginx on this site. If you didn’t hear about Let’s Encrypt you should check out their website. They offer free SSL certificates, so there is no valid reason to not have an SSL certificate anymore. There are various tutorials on how to setup Let’s encrypt with nginx.

The problem

I managed to create a certificate for this blog without much struggle, but when it came to renew it I ran into a problem. I received an “unauthorized” error. The certbot was not able to reach /.well-known/acme-challenge and got a 404 error. I noticed it was redirected to HTTPS which made sense. After setting up SSL I added redirect rules to redirect from HTTP to HTTPS which looks like this:

server {
  listen [::]:80;
  listen 80;

  # listen on both hosts
  server_name larsgraubner.com www.larsgraubner.com; 

  # and redirect to the https host (declared below)
  # avoiding http://www -> https://www -> https:// chain.
  return 301 https://larsgraubner.com$request_uri;
}

To make Let’s Encrypt authorize via HTTP I added the following block to my redirection server block.

location /.well-known/acme-challenge {
  root /usr/share/nginx/html;
  allow all;
}

Should work, right? It did not. It was still redirecting to HTTPS and failing the renew process. After trying out various things I finally found a solution.

The solution

I’m not a nginx expert, but the following is working fine for me:

server {
  listen [::]:80;
  listen 80;

  # listen on both hosts
  server_name larsgraubner.com www.larsgraubner.com; 

  location /.well-known/acme-challenge {
    root /usr/share/nginx/html;
    allow all;
  }

  location / {
    # and redirect to the https host (declared below)
    # avoiding http://www -> https://www -> https:// chain.
    return 301 https://larsgraubner.com$request_uri;
  }
}

It seems like return is called every time, even when an location block matches. Wrapping the return in another location block fixes the problem as the .well-known block matches first and ignores all following location blocks.

This way you can renew your Let’s Encrypt certificates even on running production sites. You can even automate it and don’t have to worry about it at all.