Discourse + Let's Encrypt w/ multiple hostnames

When migrating (via backup) a Discourse from one hostname to another, an admin might want to keep the old hostname but have the web server rewrite requests to use the new canonical name, e.g.:

https://old.example.org/t/my-great-topic/12345 :arrow_heading_down:
https://new.example.org/t/my-great-topic/12345

Unfortunately this isn’t as easy as just changing the CNAME or A record in DNS; the browser client will generate an error/warning because the hostname does not match the certificate generated by the Discourse installation via Let’s Encrypt.

On a manual installation of a web server, I’d use the cerbot utility to generate a certificate with multiple names assigned to it. However, the Discourse configuration passes the Discourse hostname (only) to Let’s Encrypt, and there is no concept that I can see in the Discourse configuration for aliases.

Has anyone set up such an arrangement, or have a hunch about how best to achieve it? I would imagine it will involve some hacking of the SSL & Let’s Encrypt templates.

2 Likes

Not Discourse-specific, but when I need to redirect over HTTPS I just redirect everything. Meaning having a server doing the redirect from the old address to the new. Some hosting options take care of this in the background, sometimes called a “domain redirect” or “forwarding” service.

I don’t even think about this anymore, because it is such a pain each time I’ve tried anything else. :thinking:

2 Likes

Discourse will only answer to one hostname. If you want more than one hostname - where your use case, supporting the former hostname, is the most common - then you will need to redirect the one to the other.

I recommend doing that outside of Discourse. Just create a nginx configuration that will have its own certificate, has a server_name for the old hostname, and redirects everything to the new hostname.

3 Likes

Actually if you’re using a single-instance-per-server Docker-based installation, it will answer fine to any number of DNS entries (nginx is listening on port 80 and 443 for all hostnames), and correctly rewrite the URL to the canonical “new” hostname just fine. That part is working fine and painlessly. (Folks who are inclined to test this out could try it by adding a fake domain name to your machine’s localhost file and point it to an IP address of your favorite Discourse site.)

The only problem I am running into is the SSL warning because the rewrite respose from nginx is from https://old.example.org/foo and sends a HTTP 302 redirect to the new URL.

I would rather not have to maintain a separate web server just to do a rewrite rule, if possible. :slight_smile:

1 Like

You don’t need to, you just need an extra server section in your config.

2 Likes

Maybe have a look at Setting up Let’s Encrypt with Multiple Domains

2 Likes

Update: Successfully configured the secondary domain name on the standalone installation, and issued a Let’s Encrypt certificate to handle both old and new names.

Precondition: I did not yet change DNS to point folks over to the new site; I wanted things to be sure to work ahead of time. So I was using a localhost entry on my testing machine. This means I could not do a normal Let’s Encrypt verification, and instead had to use the acme.sh “DNS” method which is generally not recommended because it can’t auto-renew. This is fine for me; because I’ll be switching over within 30 day term of the “initial” (newly-issued 2-name) certificate.

  1. In my web_only.yml file (you may be using app.yml) under the hooks: section, and before the plugins, added the following:
  after_ssl:
    - replace:
        filename: "/etc/runit/1.d/letsencrypt"
        from: /--keylength/
        to: "-d second-domain.com --keylength"
  1. In DNS, set up a bogus/temporary TXT record for _acme-challenge.old.example.org with a 5-minute TTL (you could probably set it shorter) and let it propogate so to quickly make the change with the acme.sh (Let’s Encrypt) verification key.

  2. Stop the site and issue a debug (test) validation to get the system aware of the new entry’s short TTL:

./launcher enter app
sv stop nginx
/usr/sbin/nginx -c /etc/nginx/letsencrypt.conf
LE_WORKING_DIR=/shared/letsencrypt DEBUG=1 /shared/letsencrypt/acme.sh --issue -d new.example.org -d old.example.org -k 4096 -w /var/www/discourse/public --dns --yes-I-know-dns-manual-mode-enough-go-ahead-please --force
  1. Run the request for real this time without the debug setting. This will give you a warning with the value to use in DNS for the entry you created in step 2:
LE_WORKING_DIR=/shared/letsencrypt /shared/letsencrypt/acme.sh --issue -d new.example.org -d old.example.org -k 4096 -w /var/www/discourse/public --dns --yes-I-know-dns-manual-mode-enough-go-ahead-please --force
  1. Update DNS and then wait 5 minutes. Yes, you should wait the whole time so you don’t run into Let’s Encrypt’s limitations.

  2. Run a similar version to the last command but in renew mode:

LE_WORKING_DIR=/shared/letsencrypt /shared/letsencrypt/acme.sh --issue -d new.example.org -d old.example.org -k 4096 -w /var/www/discourse/public --dns --yes-I-know-dns-manual-mode-enough-go-ahead-please --force --renew
  1. You should have a confirmation. Run the following to clean things up and get the new cert in place:
LE_WORKING_DIR=/shared/letsencrypt /shared/letsencrypt/acme.sh --installcert -d new.example.org -d old.example.org --fullchainpath /shared/ssl/new.example.org.cer --keypath /shared/ssl/new.example.org.key --reloadcmd "sv reload nginx"
/usr/sbin/nginx -c /etc/nginx/letsencrypt.conf -s stop
exit
  1. Last step now that you’ve left the launcher:
rm -rf /var/discourse/shared/standalone/ssl
rm -rf /var/discourse/shared/standalone/letsencrypt
./launcher rebuild app

Once the site is back up, it should be running with the new cert. You may have to clear out your browser’s store certificates, which is left as an exercise to the reader and your favorite search engine.

Thanks to folks who pointed me in the right direction!

2 Likes

This is a perfect application for a Cloudflare rule - zero server configuration. Just throw the same URL parameters at the new domain.

The orange cloud will need to be enabled for the old domain name.

2 Likes

Yeah, a reverse proxy situation like Cloudflare (or I suppose your own reverse proxy of choice in front of your Discourse site) could workaround it by implementing the rewrite rules “up a level”. In this case Cloudflare was not an option for security and moral reasons. :slight_smile:

(See above for standalone solution.)

2 Likes

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