Setting up HTTPS support with Let's Encrypt

:exclamation: This guide is only for existing installs where HTTPS is not enabled. Following the official setup guide automatically enables HTTPS by default.

So you’d like to add https to your Discourse absolutely free, courtesy of our friends at Let’s Encrypt?

:bell: Is everything else on your site ready for HTTPS?

Before you start, please bear in mind that for HTTPS to work properly, every single resource on the page must be HTTPS compatible. Consider your CDN, your social logins, your logo files, any third party JavaScript, images, fonts, or css — these all must be available over HTTPS!

Note: ./discourse-setup will enable Let’s Encrypt. And as of March 2017, you can run it again, and press return a few times and enter your email address ; the script will include the required templates and insert your email address as required. Unless you are an expert sysadmin and know a reason not to do that, you should run discourse-setup rather than read any further. (If you installed Discourse a long time ago, you might still have to edit app.yml by hand.)

Note: If your Discourse is accessed via some reverse proxy (e.g., Cloudflare) this will not work.

For the next few steps, you will be editing the app.yml file for your Discourse instance:

cd /var/discourse
nano containers/app.yml

1. Add web.ssl.template.yml and web.letsencrypt.ssl.template.yml to templates

:warning: Is Discourse the only website on your server?

If you are already using web.socketed.template.yml, because you host other websites via port 80 on the same server, stop. You should be using a Let’s Encrypt client on the host system; the validation will fail as the client used is unable to bind to the necessary sockets.

  - "templates/web.template.yml"
  - "templates/web.ssl.template.yml"
  - "templates/web.letsencrypt.ssl.template.yml"

2. Expose port 443

  - "80:80"
  - "443:443"

3. Add email account to register with Let’s Encrypt


4. Rebuild your container

./launcher rebuild <container name>

After that completes, load the site in your browser using https:// – it should “just work!” :tada:

5. Adjust your dependencies for HTTPS

  • Check that all shared resources such as images, logos, third party JavaScript dependencies, etc, are all linked via https:// and not http://

  • Check all your social logins and make sure they are pointing to the https:// and not http:// versions of your site’s authentication URLs.

  • If you are using a CDN, you will need to switch your CDN to use https:// and not http:// to pull resources.

  • If your browser does not show any warnings in its f12 console in the security about insecure assets:

    … you are now ready to force HTTPS by going to

    Admin :arrow_right: site settings :arrow_right: force https

How does it work?

The template uses which is

Simplest shell script for LetsEncrypt free Certificate client

Simple and Powerful, you only need 3 minutes to learn.

Pure written in bash, no dependencies to python , acme-tiny or LetsEncrypt official client. Just one script, to issue, renew your certificates automatically.

Probably it’s the smallest&easiest&smartest shell script to automatically issue&renew the free certificates from LetsEncrypt.

web.letsencrypt.ssl.template.yml adds a startup script to your container that

  1. Issues a Let’s Encrypt cert using the standalone mode. It boots a standalone server that listens on port 80 but this happens before nginx is up so port 80 is free.
  2. Installs the cert into the right directory that nginx expects. At the same time, it adds a cron job that runs a daily cert renewal check. This will automatically renew your cert. Nothing happens if cert has not expired. If the certificate does expire, you’ll get an email about it from Let’s Encrypt at the email address you provided during setup.
  3. Switches the script to use the webroot plugin with /var/www/discourse/public as the directory. This will allow us to use nginx as the server that handles domain validation. Zero downtime during cert renewal!


Check logs for cert related errors

./launcher logs <container name>

Then look for any errors mentioning letsencrypt or SSL.

Did the cert files get written OK?

ls -l /var/discourse/shared/standalone/ssl on the host server

You should have a certificate .cer and key .key file for your domain, and they should have ~3k filesize.

-rw-r--r-- 1 root root  424 May 18 03:22 dhparams.pem
-rw-r--r-- 1 root root 3823 May 18 04:24
-rw-r--r-- 1 root root 3243 May 18 04:24

Manually reissue the cert

./launcher enter app
sv stop nginx
/usr/sbin/nginx -c /etc/nginx/letsencrypt.conf
LE_WORKING_DIR=/shared/letsencrypt DEBUG=1 /shared/letsencrypt/ --issue -d -k 4096 -w /var/www/discourse/public
LE_WORKING_DIR=/shared/letsencrypt /shared/letsencrypt/ --installcert -d --fullchainpath /shared/ssl/ --keypath /shared/ssl/ --reloadcmd "sv reload nginx"
/usr/sbin/nginx -c /etc/nginx/letsencrypt.conf -s stop

Delete the old cert files and try rebuilding again

rm -rf /var/discourse/shared/standalone/ssl
rm -rf /var/discourse/shared/standalone/letsencrypt
./launcher rebuild app


Though the Let’s Encrypt certificate is valid and safe for encrypting data, it does not certify that the site is owned by a particular organization. Firefox will complain that the certificate “does not supply ownership information.” This is a limitation of how Let’s Encrypt works. For more information see: Adding Ownership Information? - Help - Let's Encrypt Community Support.

Setting up Let's Encrypt for multisite
How should I enable letsencrypt while discourse is beside other websites
Setting up Let’s Encrypt with Multiple Domains
Advanced Setup Only: Allowing SSL / HTTPS for your Discourse Docker setup
Add to homescreen banner on Android
Completely automated SSL certificate generation
Issue installing on subdomain
Cant setting SLL (Let's Encrypt) for Discourse
Setting up SSL with my domain name and Discourse instance
Replacement for whitelist-iframe
German 1&1-hosting user experience?
Site throws a blank screen after trying to enable LetsEncrypt
Setting up Let’s Encrypt with Multiple Domains
How to Set Up SSL in Discourse
SSL Help for subdomain install?
Let’s Encrypt + non-www > www
Using a certificate when Discourse is installed behind a reverse proxy
Can discourse be installed in private mode
My Forum Is showing "Privacy Error" after upgrading SSL certificate
Using a certificate when Discourse is installed behind a reverse proxy
Why my forum not pop-up "Add to Home screen"(PWA) automatically?
Why my forum not pop-up "Add to Home screen"(PWA) automatically?
HTTPS : issue while trying to set up SSL certification
HTTPS : issue while trying to set up SSL certification
How to deny request from unauthorized domain pointing to my IP address?
SSL received a record that exceeded the maximum permissible length
I cannot check docker name (● ˃̶͈̀ロ˂̶͈́)੭ꠥ⁾⁾
Discourse site loads via IP but via domain only header
Why is the Apple Touch Icon loaded via HTTP instead of HTTPS?
Defaultish app won't rebuild
Why is the Apple Touch Icon loaded via HTTP instead of HTTPS?
Why is the Apple Touch Icon loaded via HTTP instead of HTTPS?
Not able to access site after letsencrypt cert expiry and rebuild due to IPV6
Unable to connect Discourse and WordPress
I have a very difficult problem installing ssl - please help
How to install Discourse in the AWS EC2 Instances(Ubuntu Server LTS)?
Cannot connect to IP address and no errors in log
Http logo urls after enabling LetsEncrypt
How to install SSL certificate in Discourse
[SOLVED] 443 address already in use? Letencrypt
White blank page on mobile app
My forum goes offline after removing https
Trying to use Let's Encrypt + Cloudflare
New user invite links only give ERR_SSL_PROTOCOL_ERROR
[DigitalOcean] hostname having "www" in A records showing blank page
Importing from phpBB3
Not starting up after rebuild
Port 443 of computer does not appear to be accessible
Uncaught ReferenceError: Discourse is not defined due to Cloudflare Rocket Loader
Unable to change domain name
Defaultish app won't rebuild
Problem with my SSL certificate
Question about the email configuration
Setting up Let’s Encrypt with Multiple Domains
Discourse-saml: There was an error authorizing your account
Problem in installing Let's Encrypt SSL for www and non-www
Setting up Let’s Encrypt with Multiple Domains
Email not sending out after installation. I need help please
Discourse not starting up: nginx: unable to open supervise/ok: file does not exist
Failed to bootstrap: Failure with receiving network data
Error when installing ssl Let's Encrypt
How to adjust dependencies for https?
Clicking links is stuck in click tracking, shows ERR_FAILED
Minimum needed to get LetsEncrypt working on a GCE instance
Can we install discourse in another container distro?
Problem with my SSL certificate
Cant setting SLL (Let's Encrypt) for Discourse
[PAID] setup ssl - Let's encrypt
Error after moving from HTTP to HTTPS
How to renew Let's Encrypt?
ServiceWorker script evaluation failed due to HTTP (not HTTPS)
Defaultish app won't rebuild
Too many redirects after enabling https
Troubleshooting email on a new Discourse install
Missing file(discourse.conf) when launching after lets encrypt update?
Missing file(discourse.conf) when launching after lets encrypt update?
My discourse has either been hacked or catfished?
Discourse has stopped opening

When discourse is setup as a root domain (e.g. without “www”), the certificate is issued to the root domain only. Is there a way of configuring this so that both the root and the www versions work - e.g. so that we don’t get certificate errors on when discourse is setup for

I believe Neil’s script allows for this using the -d option to specify multiple hosts for the certificate (so a SAN is used in the cert), so possible that we need to cater for this in the web.letsencrypt.ssl.template.yml somehow. Maybe by adding another variable into app.yml (e.g. DISCOURSE_ALT_HOSTNAME: ‘’), and then pick this up in the script in web.letsencrypt.ssl.template.yml.

Meanwhile, can we manually run commands to do this without anything being overwritten on restart? Or is it okay to edit the conf file for the domain and set a value for Le_Alt ? If we alter the value for Le_Alt, how do we then force a reissue and install of the cert?



Adding multiple domains has been done before. I’ll like to bake this into the template though, we should probably take in an additional env variable and add that into the issue command. Hopefully someone from the community can take this? :stuck_out_tongue:


@tgxworld Thanks for the link. If I make these changes, I’m assuming that the script won’t update the cert until renewal because it will see a valid certificate already installed. How do I force it to update?


1 Like

Run the issue command manually with FORCE=1 :slight_smile:


@tgxworld Thanks for the tip.

I didn’t implement the file changes in app.yml, partly because I wanted to keep the build as standard as much as possible.

However, using the info from that post and your tip on FORCE=1, I did the following (posted here just in case someone else wants to do the same):
./launcher enter app vi /etc/runit/1.d/install_ssl_cert sv stop nginx FORCE=1 /etc/runit/1.d/install_ssl_cert sv start nginx

In the edit of install_ssl_cert, I changed the first line (after #!/bin/bash) to:
LE_WORKING_DIR="/shared/letsencrypt" /shared/letsencrypt/ issue no 4096

Checked the certificate after install and it is now complete with and without “www”.

Thanks for your help.


Additional note to noobs running CloudFlare DNS:

You need to enable Full SSL in CloudFlare settings, or your site won’t work.

Installation wen’t smoothly and running this now on my small side project. Giving it some more time to mature, before enabling on my big site.

Once again, great job guys!


Is it possible to (easily) change when the script agrees to renew the script?

At the moment, it looks like the cert renewal is about 9 days before it expires. In the meantime, Let’s Encrypt has sent me two reminder emails saying that the cert is going to expire.

I’d prefer to have the cert renewed as soon as it possibly can so that I only get the LE reminder emails if, for some reason, the renewal process has failed and I therefore need to investigate further.



On my Discourse server, the auto-renew cron job seems to be broken. I’ve grabbed a shell:

./launcher enter app

and then tried to run a renewal myself:

"/shared/letsencrypt"/ --home "/shared/letsencrypt" --renew-all
[Fri Sep  8 10:50:23 UTC 2017] Renew: ''
[Fri Sep  8 10:50:23 UTC 2017] Single domain=''
[Fri Sep  8 10:50:23 UTC 2017] Getting domain auth token for each domain
[Fri Sep  8 10:50:23 UTC 2017] Getting webroot for domain=''
[Fri Sep  8 10:50:23 UTC 2017] Getting new-authz for domain=''
[Fri Sep  8 10:50:24 UTC 2017] The new-authz request is ok.
[Fri Sep  8 10:50:25 UTC 2017]
[Fri Sep  8 10:50:30 UTC 2017] error:Invalid response from 
[Fri Sep  8 10:50:30 UTC 2017] Please check log file for more details: /shared/letsencrypt/

Looking in the log (which is pretty huge, but I can paste if needed) it appears to be writing the token, and then failing anyway.

My cert expires tonight (but it’s not a huge deal, we only have a few users), so any advice is appreciated!

Of course, as soon as I post, I find the issue. My site only passes 443 to Discourse as other things run on 80 - and of course the renewal has to happen on 80… as soon as I added a vhost on 80 to handle .well-known, all was fine. Thanks anyway!


Do I need to add any 301 redirects to let Google know about the move from http to https? Or it happens automatically?

AFAIK it happens auto-magically. Discourse automatically takes care of the 301 redirects.

That’s why it is No-Brains-Required™. :grinning:


I just set up a site with Let’s Encrypt and Firefox is whining that it “does not supply ownership information”.

Screenshot from 2017-10-05 09-32-03

Screenshot from 2017-10-05 09-35-27

But, there’s nothing that can be done, as explained here: Adding Ownership Information? - Help - Let's Encrypt Community Support

we were using this set up in order to have https. but then as we turned off cloudflare, we got Your connection is not secure error. and an expiration date for the ssl certificate.


  1. is the output effect of this howto document the same as this one: Advanced Setup Only: Allowing SSL / HTTPS for your Discourse Docker setup? I mean both of them seem to allow for https.

  2. what is about the expiration date of a ssl? nothing about an expiration date is reported in this howto.

I followed this guide after setting up a new discourse “one-click” droplet on Digital Ocean. Just wanted to say thanks, it worked perfectly. Pretty awesome that uncommenting a few lines and restarting gives you SSL for free!


Thanks, Greg. Turns out I only had 443 open to the world as well. Once I opened up 80, voila, I was able to renew the Let’s Encrypt Cert.


If we are using Cloudflare, can’t we use Let’s Encrypt? Is not there a way?

Yes, read this post.


I just enabled Let’s Encrypt and SSL appears to be working, but there is an error in the console I don’t understand, and don’t know whether it’s related to SSL. Link below.

Note, the error takes a minute or two before it appears in my console.

This is the error I get in chrome console:

9/t/morning-i-ll-check/19:1 Failed to load
Request header field X-CSRF-Token is not allowed by
Access-Control-Allow-Headers in preflight response.

Something’s triggering a CORS preflight, which shouldn’t be happening because it’s all on the same domain, right?

1 Like