Last updated 31 January 2017
Table of Contents
SSL is a cryptographic protocol that provides end-to-end encryption and integrity for all web requests. Apps that transmit sensitive data should enable SSL to ensure all information is transmitted securely.
Heroku SSL is included for free on any app that uses paid dynos: Hobby, Standard-1X, Standard-2X, Performance-M and Performance-L. This service uses Server Name Indication (SNI), an extension of the TLS protocol, which allows for Heroku to terminate SSL on its router.
An alternative SSL implementation is available via the SSL Endpoint add-on. The SSL Endpoint add-on is only recommended if you need to support legacy browser clients which do not support SNI. Our default recommendation is to use the Heroku SSL described in this document.
Because of the unique nature of SSL validation, provisioning SSL for your application is a multi-step process that involves several third parties. You will need to:
- Acquire an SSL certificate from your SSL provider
- Upload the certificate to Heroku
- Update your DNS settings to reference the new SSL endpoint
Add certificate and intermediaries
Add your certificate, any intermediate certificates bundles, and private key with the
$ heroku certs:add server.crt server.key Adding SSL to example... done exampleapp now served by www.example.com.herokudns.com. Certificate details: Expires At: 2012-10-31 21:53:18 GMT Issuer: C=US; ST=CA; L=SF; O=Heroku; CN=www.example.com Starts At: 2011-11-01 21:53:18 GMT ...
At this point, you can verify that your application is serving your certificate by running:
$ openssl s_client -connect <dns target>:443 -servername <your domain> # e.g. openssl s_client -connect www.example.com.herokudns.com:443 -servername www.example.com
Once you have verified that your app is serving your certificate, you must update your DNS settings for each domain on your app. The DNS entry assigned to your app’s domains will be listed after you add your certificate, using the pattern
<your domain>.herokudns.com. In the above example, it would be
www.example.com.herokudns.com If you need to review the DNS targets again, simply run
heroku domains to see where you should set it up.
If your certificate does not match any domains on your application, you will need to add a matching custom domain in order to use your certificate.
Heroku does not allow private keys to be extracted out of its systems. Make sure to keep a copy safely for yourself in case it needs to be reused anywhere.
SSL certificate details
To get the detailed information about a certificate, use
$ heroku certs:info Fetching SSL tyrannosaurus-87601 info for exampleapp... done Certificate details: Expires At: 2012-10-31 21:53:18 GMT Issuer: C=US; ST=CA; L=SF; O=Heroku; CN=www.example.com Starts At: 2011-11-01 21:53:18 GMT Subject: C=US; ST=CA; L=SF; O=Heroku; CN=www.example.com ...
Verify SSL is setup correctly
Use a command line utility like
curl to test that everything is configured correctly for your secure domain.
$ curl -vI https://www.example.com * About to connect() to www.example.com port 443 (#0) * Trying 18.104.22.168... connected * Connected to www.example.com (22.214.171.124) port 443 (#0) * SSLv3, TLS handshake, Client hello (1): * SSLv3, TLS handshake, Server hello (2): * SSLv3, TLS handshake, CERT (11): * SSLv3, TLS handshake, Server finished (14): * SSLv3, TLS handshake, Client key exchange (16): * SSLv3, TLS change cipher, Client hello (1): * SSLv3, TLS handshake, Finished (20): * SSLv3, TLS change cipher, Client hello (1): * SSLv3, TLS handshake, Finished (20): * SSL connection using AES256-SHA * Server certificate: * subject: C=US; ST=CA; L=SF; O=SFDC; OU=Heroku; CN=www.example.com * start date: 2011-11-01 17:18:11 GMT * expire date: 2012-10-31 17:18:11 GMT * common name: www.example.com (matched) * issuer: C=US; ST=CA; L=SF; O=SFDC; OU=Heroku; CN=www.heroku.com * SSL certificate verify ok. > GET / HTTP/1.1 > User-Agent: curl/7.19.7 (universal-apple-darwin10.0) libcurl/7.19.7 OpenSSL/0.9.8r zlib/1.2.3 > Host: www.example.com > Accept: */* ...
Pay attention to the output. It should print
SSL certificate verify ok. If it prints something like
common name: www.example.com (does not match 'www.somedomain.com') then something is not configured correctly.
If you use the
-k flag with the curl command, it will result in a connection error if are running curl on many versions of Mac OS X as SNI information is not passed with
You can update a certificate using the
certs:update command with the new certificate and the new or an existing private key:
$ heroku certs:update server.crt server.key Updated certificate details: Common Name(s): www.example.com Expires At: 2017-03-21 21:35 UTC Issuer: /C=US/ST=California/L=San Francisco/O=Heroku/OU=Engineering/CN=www.example.com Starts At: 2016-03-21 21:35 UTC Subject: /C=US/ST=California/L=San Francisco/O=Heroku/OU=Engineering/CN=www.example.com
You can remove a certificate using the
$ heroku certs:remove Removing SSL certificate tyrannosaurus-87601 (www.example.com.herokudns.com) from example... done
Removing a certificate will cease any HTTPS traffic to the certificate’s domain.
Migrate from SSL:Endpoint to Heroku SSL
You can migrate from the SSL:Endpoint add-on to Heroku SSL with zero downtime.
Add the SSL Certificate to your app
$ heroku certs:add example.crt example.key --type sni
You need to use the flag
--type sni if your app already has the SSL Endpoint add-on.
Change your DNS for all domains on your app
Verify your DNS settings by running
dig www.yourdomainname.com cname +short. If it returns
www.yourdomainname.com.herokudns.com then you have set it up correctly. If you are using an ALIAS or ANAME record, you can verify that based on the DNS provider. For instance, DNSimple will return a TXT record showing how your domain’s ALIAS is set-up.
You should note that it may take up to 24 hours before your DNS is fully propagated globally.
Remove SSL:Endpoint Add-on after 24 hours
If it has been 24 hours since you updated your DNS settings, then you can remove the SSL:Endpoint add-on.
$ heroku addons:destroy ssl
If you ever need to switch back to SSL Endpoint, after you, you can follow the steps in the SSL Endpoint article.
Client IP address
When an end-client (often the browser) initiates an SSL request, the request must be decrypted before being sent to your app. This extra SSL termination step obfuscates the originating IP address of the request.
As a workaround, the IP address of the external client is added to the
X-Forwarded-For HTTP request header.
Supported SSL protocols
- TLS 1.0
- TLS 1.1
- TLS 1.2
We do not support SSLv3 due to security reasons.
Minimum supported browser versions
SNI SSL termination has minimum browser versions that it needs to support. If you have a browser that is less than one of these versions, it will result in a connection error. It is worth noting that the amount of traffic from incompatible browsers is considerably small, less than 0.1% of overall platform traffic. If you need to support legacy versions, then consider using the SSL Endpoint add-on.
- Firefox 2 and above
- Internet Explorer 7 on Windows Vista and above
- Windows Vista or OS X 10.6 with:
- Chrome 5.0.342.0
- Opera 14
- Safari 4
- Mobile Safari on iOS 4.0 and above
- Android 4.0 (“Ice Cream Sandwich”) and above
- Windows Phone 7 and above
Internal server error
If you get an
Internal server error when adding your certificate, it may be that you have an outdated version of the Heroku CLI.
Verify your CLI installation and update it to the latest version with
heroku update, or reinstall it.
SSL file types
Many different file types are produced and consumed when creating an SSL certificate.
.csrfile is a certificate signing request, which initiates your certificate request with a certificate provider and contains administrative information about your organization.
.keyfile is the private key used for your site’s SSL-enabled requests.
.crtextensions are often used interchangeably and are both base64 ASCII encoded files. The technical difference is that
.pemfiles contain both the certificate and key whereas a
.crtfile only contains the certificate. In reality this distinction is often ignored.