Authorization fails for nginx with server block redirecting non-www to www

I ran this command: certbot --nginx -d "example.com" -d "www.example.com"

It produced this output: Failed authorization procedure Invalid Response

My web server is: Nginx 1.14.0

The operating system my web server runs on is: Ubuntu 18.04

My hosting provider is: DigitalOcean

I can login to a root shell on my machine: yes

I’m using a control panel to manage my site: no

The version of my client is: certbot 0.31.0

From the logs, I’ve identified why the failure occurred, though I do not know how to fix it.
I have two server blocks in the configuration file: The first is simply for redirecting the non-WWW domain to the WWW one as shown below while the second is used for actually serving the site. Authorization succeeded for the WWW site but failed for the non-WWW site

server {
    server_name example.com
    return 301 $scheme://www.example.com$request_uri
}

The reason for this failure is due to how the certbot nginx plugin handled the authorization. It inserted the response for the acme-challenge after the return 301 as shown below:

server {
    server_name example.com
    return 301 $scheme://www.example.com$request_uri
    location = /.well-known/acme-challenge/challengeurl{default_type text/plain;return 200 challengeresponse;} # Managed by Certbot
}

Since it is adding the challenge response return after the return 301, it will never reach that statement. Is there a way to force it to insert the location before the final return statement?

Stick the redirect inside a

location / {
}

That allows for the peaceful coexistence of the two rules.

3 Likes

Thanks! That fixed that issue, but only for IPv4. For IPv6, it still will not work. In fact, when trying to diagnose the issue using curl, I found that it returned the actual webpage that the redirect would point to had nothing followed the domain name result of the redirection, not even the 301 redirect. Any ideas for how to fix this?

Hi @Matthew

then your ipv6 configuration doesn't work. Do you have one?

Or remove the DNS AAAA entry, then create a new certificate, then fix your ipv6.

Without more information, we can’t be sure what might be happening.

But the example virtual host in your first post was only listening on IPv4. (It was also missing semicolons.)

The full configuration file is below. Early on I did attempt to add the lines listen 80; and listen [::]:80 ipv6only=true to the first server block, but when I ran nginx -t it reported duplicate listens for [::]:80 .

server {
    server_name example.com;
    location / {
        return 301 $scheme://www.example.com$request_uri;
    }
}

server {
    listen 80;
    listen [::]:80 ipv6only=on;
    server_name www.example.com;
    root /var/www/example.com;
    index index.php index.html index.htm index.nginx-debian.html;
    
    location = /favicon.ico { log_not_found off; access_log off; }
    location = /robots.txt { log_not_found off; access_log off; allow all; }
    location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ {
        expires max;
        log_not_found off;
    }
    
    location / {
        #try_files $uri $uri/ =404;
        try_files $uri $uri/ /index.php$is_args$args;
    }
    
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
    }
    
    location ~ /\.ht {
        deny all;
    }
}

In any case, I’m still a little confused why when accessing the non-WWW site using IPv6 I get the www site index page returned (HTTP 200) instead of a HTTP 301 to that page. If it wasn’t listeing on IPv6 at all, wouldn’t it just reset or refuse the connection?

Checking your domain, then your ipv6 - there is an Internal Server error - https://check-your-website.server-daten.de/?q=[2604%3Aa880%3A400%3Ad0%3A%3A4b30%3A5001]

http status 500

Error establishing a database connection

Your ipv4 has the same problem, that blocks creating a new certificate.

Are you sure there aren’t any other virtual hosts (listening on port on on IPv6)?

Does “sudo nginx -T” find any?

1 Like

I just ran it to be sure and the only file with server blocks it found is the one I shared above.

This is a new site, so I hadn't even setup the database yet. I have now, so that error is now corrected. However, I still can't get the certificate to be issued. For example sake, I permanent added an example acme-challenge to the config. The output of my GET request using curl for both IPv4 and IPv6 is shown below:

$ curl http://netopalis.org/.well-known/acme-challenge/cweYzwwxoZnJePI8NZM4w4CJ_mp4KXg92QLxTWgUnL4 -4i
HTTP/1.1 200 OK
Server: nginx/1.14.0 (Ubuntu)
Date: Tue, 08 Oct 2019 19:57:19 GMT
Content-Type: text/plain
Content-Length: 87
Connection: keep-alive

cweYzwwxoZnJePI8NZM4w4CJ_mp4KXg92QLxTWgUnL4.UHbWHzaWJSJSfCE7VOCW9bx2gGQupLeiI6qRPMEQTiM

$ curl http://netopalis.org/.well-known/acme-challenge/cweYzwwxoZnJePI8NZM4w4CJ_mp4KXg92QLxTWgUnL4 -6i

HTTP/1.1 301 Moved Permanently
Server: nginx/1.14.0 (Ubuntu)
Date: Tue, 08 Oct 2019 19:57:05 GMT
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
Expires: Wed, 11 Jan 1984 05:00:00 GMT
Cache-Control: no-cache, must-revalidate, max-age=0
X-Redirect-By: WordPress
Location: http://www.netopalis.org/.well-known/acme-challenge/cweYzwwxoZnJePI8NZM4w4CJ_mp4KXg92QLxTWgUnL4

It would only complain about that if the server_name was duplicated as well.

You should definitely be using the same listen directives in each of your port 80 virtual hosts.

Lacking those listen lines is my guess for what's the cause of your v4/v6 weirdness.

1 Like

I did it again just to be certain, and sure enough, as soon as I modify the config with both listen statements, running nginx -t resulted in the error

nginx: [emerg] duplicate listen options for [::]:80 in /etc/nginx/sites-enabled/netopalis.org:13
nginx: configuration file /etc/nginx/nginx.conf test failed

The full configurition file is below:

server {
    listen 80;
    listen [::]:80 ipv6only=on;
    server_name netopalis.org;
    location / {
        return 301 $scheme://www.netopalis.org$request_uri;
    }
location = /.well-known/acme-challenge/cweYzwwxoZnJePI8NZM4w4CJ_mp4KXg92QLxTWgUnL4{default_type text/plain;return 200 cweYzwwxoZnJePI8NZM4w4CJ_mp4KXg92QLxTWgUnL4.UHbWHzaWJSJSfCE7VOCW9bx2gGQupLeiI6qRPMEQTiM;}
}

server {
    listen 80;
    listen [::]:80 ipv6only=on;
    server_name www.netopalis.org;
    root /var/www/netopalis.org;
    index index.php index.html index.htm index.nginx-debian.html;

    location = /favicon.ico { log_not_found off; access_log off; }
    location = /robots.txt { log_not_found off; access_log off; allow all; }
    location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ {
        expires max;
        log_not_found off;
    }

    location / {
        #try_files $uri $uri/ =404;
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
    }

    location ~ /\.ht {
        deny all;
    }
}

You’re right. ipv6only=on can only be on a single listener.

What if you remove all traces of ipv6only=on from your entire nginx config?

2 Likes

That fixed it! Odd thing though, I had tried setting ipv6only=off and it still wouldn’t accept having it in two places

ipv6only is pretty underdocumented, but it seems to be an option that affects wildcard sockets globally.

So each time it appears more than once (regardless of being off or on), it overrides any other instances of that option, resulting in that warning.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.