I run multiple websites on Debian Jessie using Nginx server. In order to simplify automatic certificate renewal, I have enabled ACME challenge support on all virtual hosts. Every website that I host is capable of serving following URI:
In my Nginx configuration I try to include snippets as much as possible instead of creating huge .conf files for every website. For example, every server that listens to HTTP includes following snippet at the beginning of the server definition. Please note that I have specified an explicit IPv4 address, since my server has multiple IP addresses and I need to run it on a specific address only. Your configuration might be different. So this is my listen.conf file:
# ------------------------------------------------------------------------------------------------
# Listen on primary IP address
listen 1.2.3.4:80;
listen [::]:80;
# Include location directive for Let's Encrypt ACME Challenge
include /etc/nginx/snippets/letsencrypt-acme-challenge.conf;
# ------------------------------------------------------------------------------------------------
Now what about this letsencrypt-acme-challenge.conf? As I said, I wanted all my websites to support ACME challenge, so I can get a certificate for any of them. Below is the content of the letsencrypt-acme-challenge.conf file:
#############################################################################
# Configuration file for Let's Encrypt ACME Challenge location
# This file is already included in listen_xxx.conf files.
# Do NOT include it separately!
#############################################################################
#
# This config enables to access /.well-known/acme-challenge/xxxxxxxxxxx
# on all our sites (HTTP), including all subdomains.
# This is required by ACME Challenge (webroot authentication).
# You can check that this location is working by placing ping.txt here:
# /var/www/letsencrypt/.well-known/acme-challenge/ping.txt
# And pointing your browser to:
# http://xxx.domain.tld/.well-known/acme-challenge/ping.txt
#
# Sources:
# https://community.letsencrypt.org/t/howto-easy-cert-generation-and-renewal-with-nginx/3491
#
#############################################################################
# Rule for legitimate ACME Challenge requests (like /.well-known/acme-challenge/xxxxxxxxx)
# We use ^~ here, so that we don't check other regexes (for speed-up). We actually MUST cancel
# other regex checks, because in our other config files have regex rule that denies access to files with dotted names.
location ^~ /.well-known/acme-challenge/ {
# Set correct content type. According to this:
# https://community.letsencrypt.org/t/using-the-webroot-domain-verification-method/1445/29
# Current specification requires "text/plain" or no content header at all.
# It seems that "text/plain" is a safe option.
default_type "text/plain";
# This directory must be the same as in /etc/letsencrypt/cli.ini
# as "webroot-path" parameter. Also don't forget to set "authenticator" parameter
# there to "webroot".
# Do NOT use alias, use root! Target directory is located here:
# /var/www/common/letsencrypt/.well-known/acme-challenge/
root /var/www/letsencrypt;
}
# Hide /acme-challenge subdirectory and return 404 on all requests.
# It is somewhat more secure than letting Nginx return 403.
# Ending slash is important!
location = /.well-known/acme-challenge/ {
return 404;
}
I know this is an old thread, but since Google finds it for many searches I thought I'd post my recent experience.
I found the configuration above didn't work for me, using the acmetool client and nginx. The primary problem was Acme was writing the challenge file to
The combination of the acmetool logs and the Nginx logs made this obvious. Here are the two commands that helped parse the acmetool logs, from MrTen on this github page
The reason was found on Server Fault, on this question:
In case of the root directive, full path is appended to the root including the location part, whereas in case of the alias directive, only the portion of the path NOT including the location part is appended to the alias.
The nginx location block that worked for me is as below
location ^~ /.well-known/acme-challenge/ {
alias /var/www/acme-challenge/;
}
I wonder why mine works with alias instead of root. Here’s what works for me, with irrelevant guff removed. I might give this some thought some time, but not today.
server {
root /var/www/example.com;
location ^~ /.well-known/acme-challenge/ {
default_type "text/plain";
alias /var/www/acme-challenge/;
}
Update Feb 2017
I’ve been playing around some more, and root seems to work better now. Why, I’m not sure, I haven’t thought things through fully. It could be something to do with the webroot I use for this. I use and tell acmetool to use /var/www/acmetool/.well-known/acmetool/
@tomwald: Certbot and acmetool apparently behave differently, so root is appropriate for one and alias is appropriate for the other.
Certbot with “-w /foo” puts files in /foo/.well-known/acme-challenge. Apparently when acmetool is told to use “/foo”, it puts the files straight in /foo.
Well that was a FAIL:
as per nginx -t
nginx: [emerg] “root” directive is duplicate, “alias” directive was specified earlier in /etc/nginx/conf.d/my.conf
And reversing the alias/root order is also a FAIL:
nginx: [emerg] “alias” directive is duplicate, “root” directive was specified earlier in /etc/nginx/conf.d/my.conf
So it seems that root and alias are synonymous now.
OK that makes sense.
But it doesn’t explain when they can’t both be used with a single location {}
And maybe we should include the preferred/recommended use - for future readers.
You can use the standalone plugin on an alternate port, and proxy the /.well-known/acme-challenge/ prefix in your web server to the port that the standalone plugin runs on. This way, there is no need to write into the document root.
You could also try any of the non-webroot challenges, such as dns-01.
Edit: You can also tell your web server to serve /.well-known/acme-challenge from outside of your regular document root, such as /var/letsencrypt/. That way no contamination with your application dir tree.