Set up Let’s Encrypt with multiple domains / redirects

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.

48 лайков
Discourse + Let's Encrypt w/ multiple hostnames
Problem in installing Let's Encrypt SSL for www and non-www
Making 'www' work with Discourse
LetsEncrypt working without www, not with www
Full site CDN acceleration for Discourse
Two domains, conditional redirect
How to setup 301 redirect -non-www to www
Setup Let’s Encrypt + non-www > www
Best way to add a 301 redirect
(Superseded) Redirect additional domain(s) to your Discourse instance
How to create 301 redirect to a new domain
Setup discourse for the main site (www.example.com) not on a subdomain
About force www to non www
Why is the Apple Touch Icon loaded via HTTP instead of HTTPS?
DNS Settings for Digital Ocean Droplet - Root Domain?
DNS Settings for Digital Ocean Droplet - Root Domain?
SSL working on root, but not on www
Facebook login was flagged as non compliant by Facebook after change to Let's Encrypt certificate system
After activating HTTPS Discourse doesn´t work
Letsencrypt issued on every build?
DO Storage and uploaded backups not showing
Non www to www without certificate error
Non www to www without certificate error
Infinite 301 redirect with reverse proxy setup
How to add ssl to non-www domain?
Adding www. to Domain
Discourse droplet unreachable on 80,443 ports after installation on Ubuntu 20.04
Installation without subdomain not working
Forum went down, then certificate renew error regarding firewall configuration?
Discourse Failing to Work
DNS settings for www
LetsEncrypt certificate not renewing
How to redirect http to https on digital ocean droplet?
Redirect old forum URLs to new Discourse URLs using permalinks
Forwarding from https:// to https://www
Workaround for the 1 week Let's Encrypt limit
DNS settings for www
Migrate discourse from main domain --> subdomain
Migrate a XenForo forum to Discourse
How can I update the nginx config directly in discourse/docker ..?
Let's Encrypt with sub-subdomain?
Problems building a new server
Multiple hostnames for a single site for cut-over phase
Help with URLs not working on Chrome
Issue after Rebuilding Discourse on Digital Ocean Droplet
Help with URLs not working on Chrome
Error on attempting to remap links
Unable to reinstall Discourse
Can't load site in Safari
(Superseded) Redirect additional domain(s) to your Discourse instance
Lets Encrypt Certificate - Issuing Problem
Www to non www with https
(Superseded) Redirect additional domain(s) to your Discourse instance
Discourse setup completed successfully but not working due to SSL error
SSL on new host
Problem in installing Let's Encrypt SSL for www and non-www
Install fails on fresh Debian 12 installation
Setup Multisite Configuration with Let's Encrypt and no Reverse Proxy
SSL is not valid for www.domain.com
SSL is not valid for www.domain.com
Trying to set up Discourse on my website
Setup Let’s Encrypt + non-www > www
Change server to a two-container setup
Bootstrap error during Discourse install: ENOENT - /etc/runit/1.d/letsencrypt
Various multisite installation questions
Changed email service, now the forum won't resolve via IP address nor URL
Discourse not accessible - SSL Issue
Correct DNS records to set when Discourse is hosted on WWW subdomain?
Invalid certificate name when using www
PEM_read_bio_X509_AUX() failed (SSL: error:0909006C:PEM routines:get_name:no start line:Expecting: TRUSTED CERTIFICATE)
Rebuild appears to work, but site is not loading properly
Issue with app.yml file update
Problem in installing Let's Encrypt SSL for www and non-www
New install on ionos ubuntu 22.04 vps keeps failing
Connection Timeout/Security Issue
Let's Encrypt Limit (again)
Port issue - Please help
Sysadmins Index
Set Up Let's Encrypt with Second Domain for Existing Discourse Install
New domain new server and 301 redirect
Is anyone here using their Discourse instance as their entire website?
Able to receive e-mail in mail-receiver but NOT in Discourse?
I can't access after installing
Setting up Discourse
Migrating site from Personal vmware server to VPS
New install fails on Ubuntu 20.04.3 LTS
Discourse Setup Error
Www.example.com OK, but example.com NOT

Спасибо, эта настройка перенаправляет «www.example.com» на «comunnity.example.com»?
Или как мне это сделать?
У меня возникли проблемы с доменом www.example.com: я настроил DNS для перенаправления на comunnity.example.com, но это не работает в Firefox или Chrome.

2 лайка

Существует инструмент проверки перенаправлений для проверки ваших перенаправлений.

6 лайков

У меня с этим небольшие трудности. Точнее, огромные.

Я решил поэкспериментировать и добавить CDN на один из своих сайтов.

Прочитав документацию, я понял, что для моего сайта лучше переехать на поддомен, а не оставаться на текущем корне (apex domain), чтобы выполнить критерии, рекомендованные 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] Lets finalize the order.
[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., так и для корневого домена.

Этот инструмент показывает, что в моем публичном сертификате указан только один домен (что и является причиной проблемы?):

1 лайк

Вам нужно изменить часть, отвечающую за получение сертификата в Let’s Encrypt. Необходимо запросить один сертификат сразу для обоих доменов. Раньше эти инструкции работали, но, похоже, что-то изменилось в процессе запроса сертификата. Если будет получен правильный сертификат, то всё остальное заработает автоматически.

1 лайк

Если вы выполните команду certbot certificates, она покажет ваши сертификаты и домены, которые они покрывают. Если они не охватывают оба — корневой домен и www, вы можете:

  1. снова запустить certbot и создать сертификат как для корневого домена, так и для www;

Если вы выберете этот вариант, выполните certbot certificates, чтобы получить имя сертификата, который нужно удалить. Затем запустите certbot delete (имя сертификата, который нужно удалить). У вас должен остаться только новый сертификат (с корневым доменом и www).

или (самый простой способ)

  1. выполните certbot --expand -d существующий.домен -d добавленный.домен

Это обновит ваш сертификат новым, содержащим исходный домен и те, которые вы добавите с помощью флага -d.

2 лайка

Джим, команда 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);'

Я получаю только один домен, и в нём отсутствует корневой домен (apex).

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 лайк

Вот что я и пытаюсь сказать. Похоже, нужно обновить и их. Не совсем понятно, почему сейчас их несколько, но автору оригинального поста нужно добавить код для изменения всех этих шагов. По крайней мере, так мне кажется.

4 лайка

Да, я попробую это сделать, сначала вручную.

2 лайка

Отлично. Я как раз собирался это сделать. Может, я вас и подтолкнул к этому. :winking_face_with_tongue:

1 лайк

Стоит попробовать, но, возможно, мне придётся пересобрать контейнер, чтобы установить nano… :sweat_smile:

1 лайк

Нет!

 apt-get update; 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 лайк