Under some circumstances you may encounter an error while getting a certificate from Letsencrypt with Type: unauthorized and Detail: Invalid response from
Most often it happens when website’s root is different from webserver’s root. Good example are Laravel based applications where website’s root is often in /public subfolder. In such case ACME challenges will be redirected to /public resulting in the error:
1 2 3 4 |
... Type: unauthorized Detail: Invalid response from https://domain.com/.well-known/acme-challenge/MI7iKrhmsVdVgc9EKIoRnK2FQt3L98wWULVGb7_5U6Y [1.2.3.4]: "<!doctype html>\n<html lang=\"en\" class=\"be-light-mode\">\n <head>\n <base href=\"/\">\n\n <title class=\"dst" ... |
We can see that Certbot gets a website itself (note the HTML markup tags) instead of the generated file.
And a few lines below we can see where Certbot expects the challenge string to be:
1 2 3 |
... 2021-11-02 03:27:54,036:DEBUG:certbot._internal.plugins.webroot:Removing /home/domain.com/public_html/.well-known/acme-challenge/MI7iKrhmsVdVgc9EKIoRnK2FQt3L98wWULVGb7_5U6Y ... |
Here is Laravel’s .htaccess
file in public_html
folder:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<IfModule mod_rewrite.c> RewriteEngine On ## ## You may need to uncomment (remove #) the following line for some hosting environments, ## if you have installed to a subdirectory, enter the name here also. ## Example: for https://site.com enter: "/", for https://site.com/something enter: "/something/" ## # RewriteBase / ## ## Uncomment following lines to force HTTPS. ## RewriteCond %{HTTPS} off RewriteRule (.*) https://%{SERVER_NAME}/$1 [R,L] RewriteCond %{REQUEST_URI}::$1 ^(/.+)/(.*)::\2$ RewriteRule ^(.*) - [E=BASE:%1] RewriteCond %{REQUEST_URI} !^public RewriteRule ^(.*)$ public/$1 [L] </IfModule> |
As we can see all URIs that don’t start with “public” are redirected to /public via rewrite rule.
So in fact
“/home/domain.com/public_html/.well-known/acme-challenge/MI7iKrhmsVdVgc9EKIoRnK2FQt3L98wWULVGb7_5U6Y”
request becomes
“/home/domain.com/public_html/public/.well-known/acme-challenge/MI7iKrhmsVdVgc9EKIoRnK2FQt3L98wWULVGb7_5U6Y”
and that’s why we can see HTML tags of the actual website in the Letsencrypt log.
To allow Letsencrypt challenges in .htaccess add a separate rule, before all others, that effectively disables rewriting for files in the directory, like this:
1 2 |
RewriteRule ^\.well-known/.+ - [END] |
You may wish to add a file existence check immediately before the rule so your custom error response page is shown rather than the server’s default:
1 |
RewriteCond %{REQUEST_FILENAME} -f |
On an Apache 2.4 vhost without a document root, it’s may be needed to add a slash after the ^
as follows: RewriteRule ^/\.well-known/.+ - [END]
So the .htaccess
that fixes Letsencrypt error “Invalid response from” will be as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<IfModule mod_rewrite.c> RewriteEngine On ## ## You may need to uncomment (remove #) the following line for some hosting environments, ## if you have installed to a subdirectory, enter the name here also. ## Example: for https://site.com enter: "/", for https://site.com/something enter: "/something/" ## # RewriteBase / ## ## Uncomment following lines to force HTTPS. ## #Don't rewrite Letsencrypt challenges! RewriteRule ^\.well-known/.+ - [END] RewriteCond %{HTTPS} off RewriteRule (.*) https://%{SERVER_NAME}/$1 [R,L] RewriteCond %{REQUEST_URI}::$1 ^(/.+)/(.*)::\2$ RewriteRule ^(.*) - [E=BASE:%1] RewriteCond %{REQUEST_URI} !^public RewriteRule ^(.*)$ public/$1 [L] </IfModule> |
Good luck!