Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

.well-known ACME challenge files blocked 403 Forbidden in some Nginx configurations #221

centminmod opened this issue Aug 29, 2015 · 8 comments


Copy link

centminmod commented Aug 29, 2015

Hi this is related to Letsencrypt manual authenticator mode with the ACME challenge file having a dot prefix certbot/certbot#730. This can be blocked with 403 Forbidden access by some Nginx configurations which block dot prefix files/folders from web access by default.


location ~ /\.          { access_log off; log_not_found off; deny all; }

For a workaround for me, I had to add this to my Nginx vhost

location ~ /.well-known {
      location ~ /.well-known/acme-challenge/(.*) {
              more_set_headers    "Content-Type: application/jose+json";

Probably need to document this for folks as to requirements needed for Nginx to allow dot prefix file for .well-known requests

not sure if you just add a curl check of the ACME challenge file for the status code so if it's anything other than 200 status, you can show a more detailed explanation ? i.e. if it's 403 status for the curl header check, say

ensure nginx vhost allows dot prefix directory access to /.well-known
Copy link

riobard commented Jan 4, 2016

You should use the following in nginx config

location ^~ /.well-known/acme-challenge/ {
  # the usual settings

Copy link

mnpenner commented Mar 6, 2016

Thank you @riobard. I was trying with

location '/.well-known/acme-challenge' { # <-- does not work
    default_type "text/plain";
    root        /tmp/letsencrypt-auto;

But that didn't do it... I guess nginx keeps processing the other restrictions. Yours seems to do the trick though.

Copy link

kb- commented Apr 8, 2016

Hi, my free webhost is blocking the " .well-known too", the dot being the problem. They only give very limited control with htaccess files and the apache configuration is of course locked... So I can't find a workaround.

Is there a way to change the challenge URL to something else who doesn't contain a dot?

Copy link

augnustin commented Apr 20, 2016

After some struggle, here's my working config:

server {
  listen                80;
  server_name ;
  root /vagrant/www/current/public;

  # Necessary for Let's Encrypt Domain Name ownership validation
  location /.well-known/acme-challenge/ {
    try_files $uri /dev/null =404;
  location / {
    return 301 https://$host$request_uri;

Copy link

wturrell commented May 30, 2016

Despite plenty of servers/configs where this works fine…

location ~ /.well-known {
  allow all;

on one where /.well-known is an alias, I have to add an ^ otherwise I get 403 Forbidden. (I can't understand why – I commented out all the other location blocks in turn, none of which match anyway, and it made no difference…)

location ^~ /.well-known {
  allow all;
  auth_basic off;
  alias /path/to/.well-known/;

Copy link

rowanthorpe commented Jul 8, 2016

The reason @riobard's version works is because of the order and manner in which nginx works through the location matches. I personally find the way it is documented at adds an unnecessary layer of temporal convolution (unless there is some nuance that I have missed when reordering the guts of points 3 and 4 below), so this is my version:

  1. If there is an exact path-match (with location = XXX {})
    • this block is processed
    • match-searching stops
  2. If there are one or more preferential prefix-matches (with location ^~ XXX {})
    • the block of the longest (most explicit) of those matches is processed
    • match-searching stops
  3. If there are one or more case-sensitive regex-matches (with location ~ XXX {}), or case-insensitive regex-matches (with location ~* XXX {})
    • the block of the first matching regex found (when scanning the config-file top-to-bottom) is processed
    • match-searching stops
  4. If there are one or more non-preferential prefix-matches (with location XXX {})
    • the block of the longest (most explicit) of those matches is processed
    • match-searching stops

The three biggest sources of confusion I have seen relevant to these kind of issues with Nginx configs are:

  • ^~ does not signify a form of regex-match despite including the ~ symbol. This surprises people coming from languages like Perl, etc.
  • The non-regex match-types are fully declarative - order of definition in the config doesn't matter - but the winning regex-match (if processing even gets that far) is entirely based on its order of entry in the config file. These two very different methods of evaluation combined within the same file raises the cognitive load a lot.
  • the files checked by try_files, error_page, etc are constructed based on the path in the context of any given root and alias directives, which sometimes become "action at a distance" gotchas

I find the following example useful (good to leave all of /.well-known open, as acme isn't the only protocol to use that directory), and of course add things like @centminmod's added header for an acme-challenge sub-match within the .well-known block if you want:

root /var/www/my-server-directory;
index index.php;  # or whatever your index files are...

location ^~ /.well-known/ {
    limit_req            [tighter per-ip settings here];
    access_log           off;
    log_not_found        off;
    root                 /var/www/html;
    autoindex            off;
    index                index.html; # "no-such-file.txt",if expected protos don't need it
    try_files            $uri $uri/ =404;

location / {
    limit_req            [looser per-ip settings here];

location ~ /\. {
    return               403;

dorianpula added a commit to dorianpula/acme-nginx that referenced this issue Dec 8, 2016
As detailed towards the end of this [issue for the Let's Encrypt ACME spec](letsencrypt/acme-spec#221).
Copy link

cpu commented Apr 4, 2017

This repository is deprecated & un-maintained. Closing this issue. If applicable, please move discussion to the replacement IETF owned repo and the mailing list.

@cpu cpu closed this as completed Apr 4, 2017
Copy link

helok commented Apr 29, 2017

location ^~ /.well-known/acme-challenge/ {
allow all;
default_type "text/plain";

@letsencrypt letsencrypt locked and limited conversation to collaborators Apr 29, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
None yet
None yet

No branches or pull requests

9 participants