使用 Let's Encrypt 设置多个域名 / 重定向

NOTE: @pfaffman says: This page needs to be cleaned up. There is now a new ENV setting that will let you add more hostnames. In your app.yml under your DISCOURSE_HOSTNAME line (it can go many places but that one makes sense), add

 DISCOURSE_HOSTNAME_ALIASES: domain.com,other.domain.com

and early reports suggest that you’ll get valid certs for those domains and that accessing https://domain.com will properly redirect you to your DISCOURSE_HOSTNAME without a certificate error.

If you do that and it works for you, you might add another “me too!” post to the bottom. If you feel comfortable, you could also edit this first post with the instructions that you think would be most helpful.


This is to address the problem where you get certificate errors with any redirects or CNAME DNS entries which point to your actual installed Discourse (sub)domain.

If you do not have https configured already (you do if you have done a standard install recently) see Setting up Let’s Encrypt as your first step.

Legacy Method

The method below no longer reliably works as of August 2025

There are three patterns that need to be replaced. Enter your (sub)domain (and any additional subdomains preceded by -d ) and then add the following to your app.yml hooks section (towards the end of the file):

2025-04-23 @pfaffman changed the code because there’s a 3rd place it needs to be changed

  after_ssl:
    - replace:
        filename: /etc/runit/1.d/letsencrypt
        from: /-d =domain1= /
        to: "-d =domain1= -d =domain2= "
        global: true

This will allow you to have HTTPS configured for a second domain that will redirect to the correct one without certificate issues.

If you need to add multiple extra domains, you can enter something like this in the domain2 field: www.bananas.com -d forum.bananas.com

For example, if you want people who visit https://forum.example.com to be redirected to your forum at https://community.example.com without a certificate error, this is all you need.

47 个赞

谢谢,这个可以用来将“www.example.com”重定向到“comunnity.example.com”吗?
或者我该如何操作?
我的域名 www.example.com 出现问题,我已配置 DNS 将其重定向到 comunnity.example.com,但在 Firefox 或 Chrome 上无效。

2 个赞

有一个重定向检查工具可以检查您的重定向。

5 个赞

我在这方面有点挣扎。嗯,很多。

我决定尝试为我的一个网站添加 CDN。

阅读文档后,我意识到为了满足 Fastly 推荐的标准(以及普遍的建议),最好将我的网站从当前的根域迁移到一个子域。

所以我想:“嗯,这很简单,我以前做过……”。是吗?:sweat_smile:

该网站是 https://starzen.space

我本周末使用此指南将其迁移到 https://www.starzen.space

一切顺利,但当然我仍然需要考虑我迄今为止在这个网站上吸引到的一小部分用户,所以我想添加一个重定向。

我的理解是,我还需要为原始链接颁发一个证书,所以按照这个指南(以前要复杂得多?),我在 app.yml 中添加了以下内容:

hooks:
  after_ssl:
    - replace:
        filename: "/etc/runit/1.d/letsencrypt"
        from: /--keylength/
        to: "-d starzen.space --keylength"
    - replace:
        filename: "/etc/nginx/conf.d/discourse.conf"
        from: /return 301 https.+/
        to: |
          return 301 https://$host$request_uri;
  after_web_config:
    - replace:
        filename: /etc/nginx/nginx.conf
        from: /sendfile.+on;/
        to: |
          server_names_hash_bucket_size 64;
          sendfile on;
    - file:
        path: /etc/nginx/conf.d/discourse_redirect_1.conf
        contents: |
          server {
            listen 80;
            listen 443 ssl;
            server_name starzen.space;
            return 301 $scheme://www.starzen.space$request_uri;
          }

重新构建后一切似乎都顺利进行。

但是,如果我尝试通过浏览器访问 https://starzen.space,我会看到:

如果我使用 curl:

blah discourse % curl https://starzen.space
curl: (60) SSL: no alternative certificate subject name matches target host name 'starzen.space'
More details here: https://curl.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.

我很确定是证书出了问题,因为如果我以不安全模式运行相同的命令,我会得到:

blah discourse % curl -k https://starzen.space

<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx/1.21.6</center>
</body>
</html>

我相信这就是我想要的。

我认为修改后的脚本是正确的,这是我拥有的:

root@starship-enterprise:/etc/runit/1.d# cat letsencrypt 
#!/bin/bash
/usr/sbin/nginx -c /etc/nginx/letsencrypt.conf

issue_cert() {
  LE_WORKING_DIR="${LETSENCRYPT_DIR}" /shared/letsencrypt/acme.sh --issue $2 -d www.starzen.space -d starzen.space --keylength $1 -w /var/www/discourse/public
}

cert_exists() {
  [[ "$(cd /shared/letsencrypt/www.starzen.space$1 && openssl verify -CAfile <(openssl x509 -in ca.cer) fullchain.cer | grep "OK")"]]
}

########################################################
# RSA cert
########################################################
issue_cert "4096"

if ! cert_exists ""; then
  # Try to issue the cert again if something goes wrong
  issue_cert "4096" "--force"
fi

<SNIP>

我甚至在容器内从命令行运行了它,在此之前,我将所有证书文件从目标目录移到了备份目录,以便为双域运行正确的命令:

root@starship-enterprise:/etc/runit/1.d# ./letsencrypt 
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] still could not bind()
[Sun 25 Sep 2022 05:50:04 PM UTC] Using CA: https://acme-v02.api.letsencrypt.org/directory
[Sun 25 Sep 2022 05:50:04 PM UTC] Creating domain key
[Sun 25 Sep 2022 05:50:05 PM UTC] The domain key is here: /shared/letsencrypt/www.starzen.space/www.starzen.space.key
[Sun 25 Sep 2022 05:50:05 PM UTC] Multi domain='DNS:www.starzen.space,DNS:starzen.space'
[Sun 25 Sep 2022 05:50:05 PM UTC] Getting domain auth token for each domain
[Sun 25 Sep 2022 05:50:08 PM UTC] Getting webroot for domain='www.starzen.space'
[Sun 25 Sep 2022 05:50:08 PM UTC] Getting webroot for domain='starzen.space'
[Sun 25 Sep 2022 05:50:08 PM UTC] www.starzen.space is already verified, skip http-01.
[Sun 25 Sep 2022 05:50:08 PM UTC] Verifying: starzen.space
[Sun 25 Sep 2022 05:50:12 PM UTC] Pending
[Sun 25 Sep 2022 05:50:15 PM UTC] Success
[Sun 25 Sep 2022 05:50:15 PM UTC] Verify finished, start to sign.
[Sun 25 Sep 2022 05:50:15 PM UTC] Le_OrderFinalize='https://acme-v02.api.letsencrypt.org/acme/finalize/590255196/128806215177'
[Sun 25 Sep 2022 05:50:16 PM UTC] Downloading cert.
[Sun 25 Sep 2022 05:50:16 PM UTC] Le_LinkCert='https://acme-v02.api.letsencrypt.org/acme/cert/03ff6b1b76f8516165032c6c2e02205a529b'
[Sun 25 Sep 2022 05:50:17 PM UTC] Cert success.
-----BEGIN CERTIFICATE-----
Lotsofcrazytext
-----END CERTIFICATE-----
[Sun 25 Sep 2022 05:50:17 PM UTC] Your cert is in  /shared/letsencrypt/www.starzen.space/www.starzen.space.cer 
[Sun 25 Sep 2022 05:50:17 PM UTC] Your cert key is in  /shared/letsencrypt/www.starzen.space/www.starzen.space.key 
[Sun 25 Sep 2022 05:50:17 PM UTC] The intermediate CA cert is in  /shared/letsencrypt/www.starzen.space/ca.cer 
[Sun 25 Sep 2022 05:50:17 PM UTC] And the full chain certs is there:  /shared/letsencrypt/www.starzen.space/fullchain.cer 
[Sun 25 Sep 2022 05:50:17 PM UTC] Installing key to:/shared/ssl/www.starzen.space.key
[Sun 25 Sep 2022 05:50:17 PM UTC] Installing full chain to:/shared/ssl/www.starzen.space.cer
[Sun 25 Sep 2022 05:50:17 PM UTC] Run reload cmd: sv reload nginx
ok: run: nginx: (pid 579) 35281s
[Sun 25 Sep 2022 05:50:17 PM UTC] Reload success
[Sun 25 Sep 2022 05:50:18 PM UTC] Domains not changed.
[Sun 25 Sep 2022 05:50:18 PM UTC] Skip, Next renewal time is: Wed 23 Nov 2022 10:01:01 AM UTC
[Sun 25 Sep 2022 05:50:18 PM UTC] Add '--force' to force to renew.
[Sun 25 Sep 2022 05:50:18 PM UTC] Installing key to:/shared/ssl/www.starzen.space_ecc.key
[Sun 25 Sep 2022 05:50:18 PM UTC] Installing full chain to:/shared/ssl/www.starzen.space_ecc.cer
[Sun 25 Sep 2022 05:50:18 PM UTC] Run reload cmd: sv reload nginx
ok: run: nginx: (pid 579) 35282s
[Sun 25 Sep 2022 05:50:18 PM UTC] Reload success

大部分成功!!!,curl 现在友好多了,给了我重定向:

blah discourse % curl https://starzen.space

<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx/1.21.6</center>
</body>
</html>

并且 https://starzen.space 在 Firefox 和 Chrome 中现在可以正常工作,重定向到正确的子域,但我仍然在 Safari 中看到那个可怕的图形,怎么回事?我甚至重新启动了它,清除了这个网站的缓存:

查看浏览器中的证书,我看到:

1 个赞

我一直想仔细看看这个。我认为现在在 Let’s Encrypt 模板中有两个地方需要添加额外的域名。我不认为你需要对 Nginx 做任何更改,因为它已经将除主机名以外的任何内容都进行了 301 重定向。

要做的是查看 Let’s Encrypt 模板,看看它在哪里放置了主机名,并确保你也使用额外的域名来完成此操作。

3 个赞

是的,谢谢,我为了完整性做了这个,而且目前似乎没有坏处,但很高兴在某个时候不带它进行重建。

似乎有两组加密文件?:

root@starship-enterprise:/shared/letsencrypt# cd starzen.space
root@starship-enterprise:/shared/letsencrypt/starzen.space# ls
backup	ca.cer	fullchain.cer  starzen.space.cer  starzen.space.conf  starzen.space.csr  starzen.space.csr.conf  starzen.space.key
root@starship-enterprise:/shared/letsencrypt/starzen.space# cd ..
root@starship-enterprise:/shared/letsencrypt# cd www.starzen.space
root@starship-enterprise:/shared/letsencrypt/www.starzen.space# ls
backup	    ca.cer	   www.starzen.space.cer   www.starzen.space.csr       www.starzen.space.key
backup_two  fullchain.cer  www.starzen.space.conf  www.starzen.space.csr.conf
root@starship-enterprise:/shared/letsencrypt/www.starzen.space# 

啊,会不会在这里(以及下面)

它只包含主机名的信息,而不是根域的信息?

1 个赞

不,我认为这是正确的?应该只有一个证书,并且它应该同时适用于 www. 和 apex。

此工具建议我的公共证书上只有一个域(这会是问题所在?):

1 个赞

您需要更改 Let’s Encrypt 获取证书的部分。它需要为两个域请求单个证书。这些说明以前是有效的,但我认为证书的请求方式发生了一些变化。如果它获取了正确的证书,那么其他一切都会正常工作。

1 个赞

如果您运行 certbot certificates,它将显示您的证书及其涵盖的域名。如果它不涵盖根域名www,您可以:

  1. 再次运行 certbot,让它为根域名和 www 创建一个证书;

如果您选择此选项,请运行 certbot certificates 获取您要删除的证书的名称。运行 certbot delete (您要删除的证书的证书名称)。您应该只剩下新的证书(包含根域名和 www)。

或者(最简单)

  1. 运行 certbot --expand -d existing.domain -d added.domain

这将使用包含原始域名和您使用 -d 标志添加的域名的新证书更新您的证书。

2 个赞

Jim,certbot 命令找不到?这是标准安装的一部分,只是路径问题吗?

1 个赞

在不实际查看的情况下……我认为 certbot 是你通常使用的,但在容器内,discourse 使用了 acme。

另外,与之相关的是,你是在容器内还是容器外尝试这个?

(另外,我今天的时间快满了,可能无法像我最初想的那样仔细查看,但这已经在我的待办事项中了)

3 个赞

同意……我认为一种方法是这样?:

如果我从任何合适的 Linux 命令行使用

true | openssl s_client -connect www.starzen.space:443 2>/dev/null \
| openssl x509 -noout -text \
| perl -l -0777 -ne '@names=/\\bDNS:([^\\s,]+)/g; print join("\\n", sort @names);'

我只得到一个域名,并且缺少根域名。

1 个赞

在容器内部,您会找到请求(并续订?)证书的代码,并看到它正在为这两个域请求证书。

1 个赞

是的,它就是这样,根据:

LE_WORKING_DIR="${LETSENCRYPT_DIR}" /shared/letsencrypt/acme.sh --issue $2 -d www.starzen.space -d starzen.space --keylength

你可以在上面的日志输出中看到这一点。

然而,还有一些其他的安装证书步骤只包含 -d www.starzen.space,这可能是一个问题?不过,如果这个证书是为两者构建的,那也许不是问题……

1 个赞

我一直在说这个问题。看起来你也需要更新那些。不确定为什么现在有多个,但 OP 需要更新代码来更改所有这些步骤。或者我这么认为。

4 个赞

是的,我打算先手动尝试一下。

2 个赞

太好了。这正是我打算做的。也许我把你骗来做这件事了。:winking_face_with_tongue:

1 个赞

尝试一下是明智的,只是我可能需要重新构建容器才能安装 nano…… :sweat_smile:

1 个赞

不!

 apt-get uodate;apt-get install nano

你只需要在容器内运行它。我一直都是这么做的(除了我用 vim)。

3 个赞

抱歉,这似乎不起作用:

root@starship-enterprise:/etc/runit/1.d# ./letsencrypt 
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] still could not bind()
[Mon 26 Sep 2022 12:35:54 PM UTC] Using CA: https://acme-v02.api.letsencrypt.org/directory
[Mon 26 Sep 2022 12:35:54 PM UTC] Creating domain key
[Mon 26 Sep 2022 12:35:56 PM UTC] The domain key is here: /shared/letsencrypt/www.starzen.space/www.starzen.space.key
[Mon 26 Sep 2022 12:35:56 PM UTC] Multi domain='DNS:www.starzen.space,DNS:starzen.space'
[Mon 26 Sep 2022 12:35:56 PM UTC] Getting domain auth token for each domain
[Mon 26 Sep 2022 12:35:59 PM UTC] Getting webroot for domain='www.starzen.space'
[Mon 26 Sep 2022 12:35:59 PM UTC] Getting webroot for domain='starzen.space'
[Mon 26 Sep 2022 12:35:59 PM UTC] www.starzen.space is already verified, skip http-01.
[Mon 26 Sep 2022 12:35:59 PM UTC] starzen.space is already verified, skip http-01.
[Mon 26 Sep 2022 12:36:00 PM UTC] Verify finished, start to sign.
[Mon 26 Sep 2022 12:36:00 PM UTC] Lets finalize the order.
[Mon 26 Sep 2022 12:36:00 PM UTC] Le_OrderFinalize='https://acme-v02.api.letsencrypt.org/acme/finalize/590255196/129044627717'
[Mon 26 Sep 2022 12:36:01 PM UTC] Downloading cert.
[Mon 26 Sep 2022 12:36:01 PM UTC] Le_LinkCert='https://acme-v02.api.letsencrypt.org/acme/cert/03ffc90cecd2f11f2ba386da2d501127aee5'
[Mon 26 Sep 2022 12:36:02 PM UTC] Cert success.
-----BEGIN CERTIFICATE-----
phewbigcert
-----END CERTIFICATE-----
[Mon 26 Sep 2022 12:36:02 PM UTC] Your cert is in  /shared/letsencrypt/www.starzen.space/www.starzen.space.cer 
[Mon 26 Sep 2022 12:36:02 PM UTC] Your cert key is in  /shared/letsencrypt/www.starzen.space/www.starzen.space.key 
[Mon 26 Sep 2022 12:36:02 PM UTC] The intermediate CA cert is in  /shared/letsencrypt/www.starzen.space/ca.cer 
[Mon 26 Sep 2022 12:36:02 PM UTC] And the full chain certs is there:  /shared/letsencrypt/www.starzen.space/fullchain.cer 
[Mon 26 Sep 2022 12:36:02 PM UTC] Installing key to:/shared/ssl/www.starzen.space.key
[Mon 26 Sep 2022 12:36:02 PM UTC] Installing full chain to:/shared/ssl/www.starzen.space.cer
[Mon 26 Sep 2022 12:36:02 PM UTC] Run reload cmd: sv reload nginx
ok: run: nginx: (pid 2970) 329s
[Mon 26 Sep 2022 12:36:02 PM UTC] Reload success
[Mon 26 Sep 2022 12:36:03 PM UTC] Domains not changed.
[Mon 26 Sep 2022 12:36:03 PM UTC] Skip, Next renewal time is: Wed 23 Nov 2022 10:01:01 AM UTC
[Mon 26 Sep 2022 12:36:03 PM UTC] Add '--force' to force to renew.
[Mon 26 Sep 2022 12:36:04 PM UTC] Installing key to:/shared/ssl/www.starzen.space_ecc.key
[Mon 26 Sep 2022 12:36:04 PM UTC] Installing full chain to:/shared/ssl/www.starzen.space_ecc.cer
[Mon 26 Sep 2022 12:36:04 PM UTC] Run reload cmd: sv reload nginx
ok: run: nginx: (pid 2970) 331s
[Mon 26 Sep 2022 12:36:04 PM UTC] Reload success

这似乎仍然暴露了一个具有单个域的证书……

true | openssl s_client -connect www.starzen.space:443 2>/dev/null \
| openssl x509 -noout -text \
| perl -l -0777 -ne '@names=/\\bDNS:([^\\s,]+)/g; print join("\\n", sort @names);'
www.starzen.space
1 个赞