# Discourse failure to renew certificate

**URL:** https://meta.discourse.org/t/discourse-failure-to-renew-certificate/385148
**Category:** Bug
**Tags:** letsencrypt
**Created:** [October 9, 2025, 3:38pm UTC](https://meta.discourse.org/t/discourse-failure-to-renew-certificate/385148 "2025-10-09T15:38:48Z")
**Posts on this page:** 20
**Page:** 1

<div class="post-metadata">

### Author: ![RBoy](https://avatars.discourse-cdn.com/v4/letter/r/2bfe46/32.png) [@RBoy](https://meta.discourse.org/u/RBoy)
#### Post date: [October 9, 2025, 3:38pm UTC](https://meta.discourse.org/t/discourse-failure-to-renew-certificate/385148/1 "2025-10-09T15:38:48Z")

</div>

Continuing the conversation from here:

> [@Letsencrypt certificate failure to renew](https://meta.discourse.org/t/letsencrypt-certificate-failure-to-renew/204885):
>
> I think it’s been years since I’ve seen a let’s encrypt certificate fail to renew, but I’ve had three sites in the past week or two that have stale certificates. A rebuild fixes it and I have heretofore failed to look for clues in the logs. Next time I’ll do a bit more to diagnose the problem before fixing it for the site.

I got a reminder from Redsift that my certificates are going to expire in a week. Usually discourse will renew the certificates well ahead of time. This time not so, before I start doing a rebuild (which is supposed to solve the issue), @Falco is there anything you want me to check and post back here to help get to the root of why the certificates did not renew?

The root certificate is ISRGX1 and here is the expiring certificate information:

| Common Name (CN) | E6 |
| --- | --- |
| Organization (O) | Let’s Encrypt |
| Organizational Unit (OU) | |
| Issued On | Wednesday, July 16, 2025 at 7:36:45 PM |
| Expires On | Tuesday, October 14, 2025 at 7:36:44 PM |

The current build is 3.6.0.beta1-dev ([7ee52c8f85](https://github.com/discourse/discourse/commits/7ee52c8f859eadbe7c1526c936bd480ebda61c71))

---

<div class="post-metadata">

### Author: ![pfaffman](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/pfaffman/32/120154_2.png) [@pfaffman](https://meta.discourse.org/u/pfaffman)
#### Post date: [October 9, 2025, 5:39pm UTC](https://meta.discourse.org/t/discourse-failure-to-renew-certificate/385148/2 "2025-10-09T17:39:26Z")

</div>

There was a period of time that the endpoint that let’s encrypt needed was redirected. That’s fixed if you rebuild.

---

<div class="post-metadata">

### Author: ![Dannii](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/dannii/32/129087_2.png) [@Dannii](https://meta.discourse.org/u/Dannii)
#### Post date: [October 22, 2025, 7:35am UTC](https://meta.discourse.org/t/discourse-failure-to-renew-certificate/385148/3 "2025-10-22T07:35:17Z")

</div>

About how long after I update should the certificate be renewed?

---

<div class="post-metadata">

### Author: ![featheredtoast](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/featheredtoast/32/116994_2.png) [@featheredtoast](https://meta.discourse.org/u/featheredtoast)
#### Post date: [October 22, 2025, 7:28pm UTC](https://meta.discourse.org/t/discourse-failure-to-renew-certificate/385148/4 "2025-10-22T19:28:21Z")

</div>

If memory serves the certificates are valid for 3 months, and they will now attempt to auto renew before then.

---

<div class="post-metadata">

### Author: ![Dannii](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/dannii/32/129087_2.png) [@Dannii](https://meta.discourse.org/u/Dannii)
#### Post date: [October 23, 2025, 7:21am UTC](https://meta.discourse.org/t/discourse-failure-to-renew-certificate/385148/5 "2025-10-23T07:21:02Z")

</div>

Yes I know how it’s meant to work.

But it’s been over a day since I updated the forum software and the certificate doesn’t appear to have been updated yet. It’s got 5 days before it expires so it really needs to be renewed soon.

I’m on the Discouse stable branch if that makes a difference. Is it possible the endpoint fix hasn’t been backported?

---

<div class="post-metadata">

### Author: ![RBoy](https://avatars.discourse-cdn.com/v4/letter/r/2bfe46/32.png) [@RBoy](https://meta.discourse.org/u/RBoy)
#### Post date: [October 23, 2025, 1:05pm UTC](https://meta.discourse.org/t/discourse-failure-to-renew-certificate/385148/6 "2025-10-23T13:05:07Z")

</div>

For me the certificate updated immediately after the rebuild

---

<div class="post-metadata">

### Author: ![Dannii](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/dannii/32/129087_2.png) [@Dannii](https://meta.discourse.org/u/Dannii)
#### Post date: [October 23, 2025, 8:27pm UTC](https://meta.discourse.org/t/discourse-failure-to-renew-certificate/385148/7 "2025-10-23T20:27:24Z")

</div>

Did you rebuild through the web or via the command line?

---

<div class="post-metadata">

### Author: ![Canapin](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/canapin/32/119591_2.png) [@Canapin](https://meta.discourse.org/u/Canapin)
#### Post date: [October 23, 2025, 8:55pm UTC](https://meta.discourse.org/t/discourse-failure-to-renew-certificate/385148/8 "2025-10-23T20:55:11Z")

</div>

Yes, the explanation was here:

> [@Let's Encrypt certificate doesn't automatically renew, but rebuilding does trigger the renewal](https://meta.discourse.org/t/lets-encrypt-certificate-doesnt-automatically-renew-but-rebuilding-does-trigger-the-renewal/383320/5):
>
> I recently worked on the le cert renewal. It is the http to https redirect - acme does not handle being told to redirect well at all and by default attempts to connect on the same protocol (http) as it did when it was setup initially Recent updates to the let’s encrypt template should fix these renewals going forward.

---

<div class="post-metadata">

### Author: ![Dannii](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/dannii/32/129087_2.png) [@Dannii](https://meta.discourse.org/u/Dannii)
#### Post date: [October 23, 2025, 10:25pm UTC](https://meta.discourse.org/t/discourse-failure-to-renew-certificate/385148/9 "2025-10-23T22:25:31Z")

</div>

My forum has finally updated its certificate after I did a command line rebuild.

---

<div class="post-metadata">

### Author: ![lessLost](https://avatars.discourse-cdn.com/v4/letter/l/a6a055/32.png) [@lessLost](https://meta.discourse.org/u/lessLost)
#### Post date: [October 26, 2025, 12:48pm UTC](https://meta.discourse.org/t/discourse-failure-to-renew-certificate/385148/10 "2025-10-26T12:48:34Z")

</div>

We have had the same experience of SSL not renewing.

It would be great if someone could double check that web.ssl.template is behaving correctly on discourse-docker, it appeared to me that port 80 was not actually serving any /.well-known/ URLs used by Let’s Encrypt, all URLs were forwarding to SSL including test files I manually placed into /var/www/discourse/public/.well-known/ . I had to edit /etc/nginx/conf.d/outlets/before-server/20-redirect-http-to-https.conf directly inside the app container.

Perhaps this started after [commit ae4887a of discourse-docker](https://github.com/discourse/discourse_docker/commit/ae4887a4f716b68f53f547f603be3b834a2b0c78)?

---

<div class="post-metadata">

### Author: ![pfaffman](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/pfaffman/32/120154_2.png) [@pfaffman](https://meta.discourse.org/u/pfaffman)
#### Post date: [October 26, 2025, 4:52pm UTC](https://meta.discourse.org/t/discourse-failure-to-renew-certificate/385148/11 "2025-10-26T16:52:31Z")

</div>

There was another error with the well known route in recent memory.

When’s the last time you did a rebuild?

---

<div class="post-metadata">

### Author: ![giuseppe.lanzi](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/giuseppe.lanzi/32/527637_2.png) [@giuseppe.lanzi](https://meta.discourse.org/u/giuseppe.lanzi)
#### Post date: [October 31, 2025, 8:44am UTC](https://meta.discourse.org/t/discourse-failure-to-renew-certificate/385148/12 "2025-10-31T08:44:25Z")

</div>

Same here. I didn’t get a warning abount the cert’s expiration. Enterine the server and launching a rebuild `/var/discourse % ./launcher rebuild` did the trick.

---

<div class="post-metadata">

### Author: ![lessLost](https://avatars.discourse-cdn.com/v4/letter/l/a6a055/32.png) [@lessLost](https://meta.discourse.org/u/lessLost)
#### Post date: [November 3, 2025, 12:26am UTC](https://meta.discourse.org/t/discourse-failure-to-renew-certificate/385148/13 "2025-11-03T00:26:45Z")

</div>

In my testing on a vanilla nginx install (1.18.0 but I think it’s the same for 1.26.3), an nginx config line `return 301 https://thehostname$request_uri;` outside of a location completely overrides any earlier `location` block before it, rather than being a catch-all. I believe `/.well-known/` simply isn’t served on port 80 unless the 301 redirect is specifically for another location such as `/` at the end of the server block. Could be the same problem as [this stackoverflow post](https://stackoverflow.com/questions/36504449/nginx-redirect-all-but-one)?

Glad rebuild works, but since the cert had already renewed for me, I couldn’t confirm that a rebuild would allow the Let’s Encrypt validation servers to get there if a cert had expired. Maybe a rebuild kicks off the cert renewal before that template line is in place or similar rather than fixing the templates, but I’m not able to confirm if that’s why rebuild gets the renewal to work.

---

<div class="post-metadata">

### Author: ![pfaffman](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/pfaffman/32/120154_2.png) [@pfaffman](https://meta.discourse.org/u/pfaffman)
#### Post date: [November 3, 2025, 2:25am UTC](https://meta.discourse.org/t/discourse-failure-to-renew-certificate/385148/14 "2025-11-03T02:25:05Z")

</div>

If you think this is a Discourse but then perhaps you should reply on the github commit or open a new big report.

---

<div class="post-metadata">

### Author: ![cedric\_chin](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/cedric_chin/32/536774_2.png) [@cedric\_chin](https://meta.discourse.org/u/cedric_chin)
#### Post date: [December 22, 2025, 4:32am UTC](https://meta.discourse.org/t/discourse-failure-to-renew-certificate/385148/15 "2025-12-22T04:32:51Z")

</div>

> [@lessLost](#):
>
> In my testing on a vanilla nginx install (1.18.0 but I think it’s the same for 1.26.3), an nginx config line `return 301 https://thehostname$request_uri;` outside of a location completely overrides any earlier `location` block before it, rather than being a catch-all. I believe `/.well-known/` simply isn’t served on port 80 unless the 301 redirect is specifically for another location such as `/` at the end of the server block. Could be the same problem as [this stackoverflow post](https://stackoverflow.com/questions/36504449/nginx-redirect-all-but-one)?

I can confirm that letsencrypt renewal fails. I’ve been running a self-hosted Discourse install for years, and very strangely renewal failed for me two times in a row over the past couple of months. The second time was this morning, and so I started investigating.

I’ve traced it to the following two commits:

[https://github.com/discourse/discourse\_docker/pull/959](https://github.com/discourse/discourse_docker/pull/959)

And relevant line linked:

[https://github.com/discourse/discourse\_docker/commit/c9064be6b7a743e3d86dbc69ddaa80701766aa87#diff-b8c95dfe3760424eecc60d21538dbd785f096d46ef95764c60f4b608f40dd536R26](https://github.com/discourse/discourse_docker/commit/c9064be6b7a743e3d86dbc69ddaa80701766aa87#diff-b8c95dfe3760424eecc60d21538dbd785f096d46ef95764c60f4b608f40dd536R26)

There are two issues, I think.

First, `return 301 https://${DISCOURSE_HOSTNAME}$request_uri;` gets turned into `return 301 https://<MY SERVER NAME>` without a `$request_uri` at the end. I have verified on my self-hosted install, and also on a friend’s self-hosted install that was set up in the past week. I don’t understand how Discourse template works, so I don’t know why it gets dropped.

Second, as @lessLost mentioned, the 301 redirect is outside of the location block. I believe a server level redirect overrides all location blocks. LetsEncrypt uses http for renewals. However, any attempt to `curl -I http://YOUR_DOMAIN/.well-known/acme-challenge/test` will return a 301 to https, instead of a 404 (which is expected behaviour; we want a 404 not a 301).

I’ve fixed this manually on my self-hosted install, but I expect any update will override my changes. Unfortunately I don’t understand the templates enough to submit a pull request @pfaffman — or I’d do that too.

Edited to add:

I believe this is mistaken —

> [@Let's Encrypt certificate doesn't automatically renew, but rebuilding does trigger the renewal](https://meta.discourse.org/t/lets-encrypt-certificate-doesnt-automatically-renew-but-rebuilding-does-trigger-the-renewal/383320/5):
>
> I recently worked on the le cert renewal. It is the http to https redirect - acme does not handle being told to redirect well at all and by default attempts to connect on the same protocol (http) as it did when it was setup initially Recent updates to the let’s encrypt template should fix these renewals going forward.

I’m fairly certain LetsEncrypt uses http by default (for obvious reasons, if the cert is expired then it can’t renew!) But placing the 301 at the server block level forces _all_ requests to 301 to https, which is inconsistent with this renewal strategy.

Edit 2: Evidence for the[http renewal strategy](https://community.letsencrypt.org/t/https-prevents-certificate-auto-renewal-why-and-what-to-do/67564/3), but you may also Google around to verify this.

---

<div class="post-metadata">

### Author: ![pfaffman](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/pfaffman/32/120154_2.png) [@pfaffman](https://meta.discourse.org/u/pfaffman)
#### Post date: [December 22, 2025, 11:49am UTC](https://meta.discourse.org/t/discourse-failure-to-renew-certificate/385148/16 "2025-12-22T11:49:36Z")

</div>

When did you last rebuild?

---

<div class="post-metadata">

### Author: ![cedric\_chin](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/cedric_chin/32/536774_2.png) [@cedric\_chin](https://meta.discourse.org/u/cedric_chin)
#### Post date: [December 22, 2025, 12:13pm UTC](https://meta.discourse.org/t/discourse-failure-to-renew-certificate/385148/17 "2025-12-22T12:13:48Z")

</div>

This morning, roughly 10 minutes after I woke up, visited my forum, and realised the cert had expired again. (Rebuilding was what renewed the last time it expired on me — roughly 3 months ago?)

---

<div class="post-metadata">

### Author: ![pfaffman](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/pfaffman/32/120154_2.png) [@pfaffman](https://meta.discourse.org/u/pfaffman)
#### Post date: [December 22, 2025, 2:21pm UTC](https://meta.discourse.org/t/discourse-failure-to-renew-certificate/385148/18 "2025-12-22T14:21:52Z")

</div>

> [@cedric\_chin](#):
>
> (Rebuilding was what renewed the last time it expired on me — roughly 3 months ago?)

I think that they’ve committed changes since then that have fixed this problem, but since it takes 3 months to find out, the jury is still out. You might set a reminder a couple weeks before the current cert expires.

---

<div class="post-metadata">

### Author: ![cedric\_chin](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/cedric_chin/32/536774_2.png) [@cedric\_chin](https://meta.discourse.org/u/cedric_chin)
#### Post date: [December 22, 2025, 2:45pm UTC](https://meta.discourse.org/t/discourse-failure-to-renew-certificate/385148/19 "2025-12-22T14:45:43Z")

</div>

This is not true.

1. The links I attached in the comment above was from a `git blame`. Here is the latest version of the file (relevant line linked): [discourse\_docker/templates/web.ssl.template.yml at 247c71a1e45d32b0b814a8e9d5fdaa4faaf727b9 · discourse/discourse\_docker · GitHub](https://github.com/discourse/discourse_docker/blob/247c71a1e45d32b0b814a8e9d5fdaa4faaf727b9/templates/web.ssl.template.yml#L37)
2. My friend’s new site install was from a week ago. Line 37 of the template above reads: `return 301 https://${DISCOURSE_HOSTNAME}$request_uri;` but in the Discourse Docker container, both her (and my) `/etc/nginx/conf.d/outlets/before-server/20-redirect-http-to-https.conf` reads `return 301 https://<our_discourse_site>;` Notice how `$request_uri` is stripped. Something is causing that to vanish! (I don’t know what).
3. I did a simulated forced renewal this morning as part of my investigation. It failed. I then changed `/etc/nginx/conf.d/outlets/before-server/20-redirect-http-to-https.conf`. It succeeded!

This is actually ok; I’ll just manually edit `20-redirect-http-to-https.conf` every time I update Discourse. For those stumbling on this comment, the command to run is:

```plaintext
cat > /etc/nginx/conf.d/outlets/before-server/20-redirect-http-to-https.conf << 'EOF'
server {
  listen 80;
  listen [::]:80;

  location ~ /.well-known {
    root /var/www/discourse/public;
    allow all;
  }

  location / {
    return 301 https://<YOUR_FORUM_ADDRESS>$request_uri;
  }
}
EOF

```

I’m not _entirely_ sure what is causing this failure, but I know modifying the conf above fixes it. But I’ve also modified notifs so that letsencrypt renewals no longer fails silently — so I can have some advanced warning. Just thought you’d like to know!

---

<div class="post-metadata">

### Author: ![nat](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/nat/32/235063_2.png) [@nat](https://meta.discourse.org/u/nat)
#### Post date: [December 22, 2025, 3:31pm UTC](https://meta.discourse.org/t/discourse-failure-to-renew-certificate/385148/20 "2025-12-22T15:31:23Z")

</div>

Thanks for the report, I think this is legit.

(fyi @featheredtoast)

[Next page](https://meta.discourse.org/t/discourse-failure-to-renew-certificate/385148.md?page=2)
