How to renew certificates automatically with apps like confluence

The issue:
I am running nginx on my ubuntu server which is proxying to a backend application, in this case, Atlassian Confluence. I am also hosting an instance of OpenProject which is set up in the same way. This means, I am not serving from a plain webroot like e.g. /var/www/html.

Now I have letsencryt certificates in place for the domains, e.g. wiki.mydomain.de (for confluence) and project.mydomain.de (for OpenProject).
However, during setup, I had to change the nginx configuration so that it is no longer proxying to Confluence/OpenProject, in order to be able to place the challenge-files in the webroot, so it could be read from certbot/letencrypt.

While this worked, and after creation of certificates I’ve switched back the nginx configuration, I am now in the very same situation again, when I need to renew the certificates automatically.

So the question is: How do I automatically renew certificates when the nginx domains only forward to backend services and are serving from a webroot folder?

I am no real nginx expert, but is there any kind of trick which can make nginx server the .well-known folder only from the webroot, but proxy the rest to the backend service?

Any helpe is highly appreciate. Otherwise I would have to prepare several nginx configs, and switch them by script before renewing certificates, which would clearly be pretty cumbersome.

So my domain config looks like this:

server {
    server_name wiki.mydomain.de;

    listen 443 ssl;
    ssl_certificate     /etc/letsencrypt/live/cloud.mydomain.de/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/cloud.mydomain.de/privkey.pem;

    add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Robots-Tag none;
    add_header X-Download-Options noopen;
    add_header X-Permitted-Cross-Domain-Policies none;
    add_header Referrer-Policy no-referrer;

    # Remove X-Powered-By, which is an information leak
    fastcgi_hide_header X-Powered-By;

    location / {
        client_max_body_size 100m;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Server $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass http://localhost:8090;
    }
}

Hi @patrickjane

that’s

your answer. You use already the location directive. Use that with /.well-known/acme-challenge, then you have a root you can use with --webroot.

1 Like

I also use nginx, Confluence and Certbot.

The solution is quite straightforward - use Certbot’s nginx plugin.

certbot --nginx -d wiki.mydomain.de

This saves you having to worry about webroots or how you are reverse proxying things. Certbot will transparently configure nginx to serve the challenge response and you’ll be on your way.

1 Like

Okay, so I have the following situation:

  1. vhost https://cloud.mydomain.de (SSL) - serves NextCloud via PHP backend
  2. vhost https://wiki.mydomain.de (SSL) - serves Confluence
  3. vhost https://project.mydomain.de (SSL) - serves OpenProject
  4. vhost In addition, I have a single vhost config for global HTTP -> HTTPS redirect:
server {
    listen 80 default_server;

    server_name _;

    return 301 https://$host$request_uri;
}

This makes all HTTP-requests redirect to HTTPS. However, it seems like certbot uses HTTP calls to verify the challenges?!

Because what I did is:

  1. create /var/www/letsencrypt/.well-known/acme-challenge
  2. add the following snippet to all 4 vhost configurations mentioned above:
    location ^~ /.well-known/acme-challenge/ {
	    default_type "text/plain";
	    root /var/www/letsencrypt;
    }
  1. reload & certbot renew

Then the renew will fail and complain about connection refused, and looking at nginx’ log, I see several requests which are only responded with HTTP301 (moved permanently), and none with HTTP200. However, when visiting the URLs mentioned in the certbot log directly in my browser (e.g. https://cloud.mydomain.de/.well-known/acme-challenge/xxxxx), I can load it just fine. I can even do it with HTTP, with 1 intermediate HTTP->HTTPS redirect.

The only way to make it work was by using the following in the 4th / global HTTPS redirect vhost:

server {
    listen 80 default_server;

    server_name _;

    location ^~ /.well-known/acme-challenge/ {
	    default_type "text/plain";
	    root /var/www/letsencrypt;
    }

#    return 301 https://$host$request_uri;  # disable redirect
}

In other words, it only works with disabled HTTPS redirect. However, of course, this also disables the HTTPS redirect.

So it seems like I am still having some issue in my config. Can someone help me out again?

Ok got it, it should look like:

server {
    listen 80 default_server;

    server_name _;

    location ^~ /.well-known/acme-challenge/ {
	    default_type "text/plain";
	    root /var/www/letsencrypt;
    }

    location / {
	    return 301 https://$host$request_uri;
    }
}

So now I have the ~ /.well-known/acme-challenge location in all 4 vhosts. Not sure if this is 100% necessary, however it works now with all subdomains functioning, HTTPS redirect functioning, and certbot renew functioning.

Thanks for the support.