Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save perki/c20d2ab990ef53b5d94cd5c42a78b81d to your computer and use it in GitHub Desktop.
Save perki/c20d2ab990ef53b5d94cd5c42a78b81d to your computer and use it in GitHub Desktop.
Fixing HTTPS for localhost - an all-purpose solution

For a WebApp or Backend developers, mixing HTTP & HTTPS while testing a live API from their local environment or the opposite with live webapp connecting to a local backend does usually end with errors such as:

  1. Mixed Content Warning:

"Mixed Content: The page at 'https://example.com' was loaded over HTTPS, but requested an insecure image 'http://example.com/image.jpg'. This content should also be served over HTTPS."

"Loading mixed (insecure) display content 'http://example.com/image.jpg' on a secure page."

  1. Blocking of HTTP Requests:

"Mixed Content: The page at 'https://example.com' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint 'http://example.com/api'. This request has been blocked; the content must be served over HTTPS."

"Blocked loading mixed active content 'http://example.com/api'."

  1. CORS Errors:

"Access to XMLHttpRequest at 'http://example.com/api'data from origin 'https://another-example.com' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header has a value 'http://another-example.com' that is not equal to the supplied origin."

"Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://example.com/api. (Reason: CORS header 'Access-Control-Allow-Origin' does not match 'http://another-example.com')"

Web browsers handle HTTP and HTTPS differently and prevent from mixing them for security reasons.

You will find 2 ways to handle this:

  1. Deactivating security checks
  2. Generating a self-signed SSL certificate and using local web server

Deactivating security checks

The main reason for not doing so is that you will mix http & https during the development phase and your work will have to be refactored before being published. You may also forget to reactivate security check and use your browser without safetly belt on.

Self-signed SSL certificate

Bellow, you find resources on how you can do it. The drawback is that you need to add a custom root certificate to your system or to your browsers to trust your self-signed one. If you need to simulate the situation with multiple hosts host1.local, host2.local you will also need to update your hosts file to point to 127.0.0.1

  • Let's Encrypt provide a well document page on generating Certificates for localhost.
  • The Gist from Cecile Muller flow presents how to generate them on your own.
  • mkcert simplify making locally-trusted development certificates.

Thanks to Let's Encrypt there is another solution

With Let's Encrypt, a domain name and DNS service with an API that allows remote update we can generate wildcard SSL certificate for this domain.

The good news is that when it's done for ones it works for everyone!

Summary of the steps:

  1. Register a domain name with all A entries pointing to 127.0.0.1 and AAAA to ::1
  2. Use let's encrypt to generate a wildcard *.domain SSL certificate
  3. Use the certificate in your development stack, from now own you can call any-name.domain and it will point out to localhost.

There is a drawback: the validity of wildcard SSL certificates provided by Let's Encrypt are limited to 30 days.

Automatization of certificates refresh and publish

backloop.dev is a domain hosted by Gandi.net a provider which offers an API to update DNS records. Thanks to Github's actions and acme-client a node.js client for Let's Encrypt we can periodically renew the certificate and publish them.

The process is the following:

  1. A workflow on github is triggered once per week
  2. The code of backloop.dev - renew is checked out
  3. The GitHub-pages branch used to publish the certificates is checked-out
  4. renew process:
    1. Generate a new SSL key and csr (Certificate Signing Request)
    2. Issues a certificate request to Let's Encrypt
    3. Receives a DNS "challenge" as a TXT record for _acme_challenge
    4. Update the domain DNS entries with the new challenge on the DNS hosting provider
    5. Returns to acme-client which activates the challenge check
    6. Receives a new certificate
  5. Posts the new certficate on the Github-pages branch
  6. Publishes the Github-pages branch

Distribution and usage

Drop-in certificates: just download them from backloop.dev - you will need to refresh them

Node.js & npm package

Backloop.dev repository contains a node.js folder published on npm with command line tools and integrations with express, node.js https and Vue.js . The tool set will take care of checking and updating the certificates

The following commands require node.js to be installed and backloop.dev installed globally with npm install backloop.dev -g. To avoid installing globally you can also prefix the command with npx or npx -p backloop.dev

  • Webserver: backloop.dev <path> [<port>] Exposes the content of a local directory on https://whatever.backloop.dev/

  • Proxy: backloop.dev-proxy <target host>[:<target port>] [<port>]

    Relays https://whatever.backloop.dev/ => http://<target host>

  • Download, check and refresh: backloop.dev-update To save the certificates in a defined location set the environment var BACKLOOP_DEV_CERTS_DIR

    Exemple: BACKLOOP_DEV_CERTS_DIR=/tmp backloop.dev-update

Integration with Node.js https server:

const https = require('https');
const httpsOptionsAsync = require('backloop.dev').httpsOptionsAsync;

httpsOptionsAsync(function (err, httpsOptions) {
  https.createServer(httpsOptions, (req, res) => {
    res.writeHead(200);
    res.end('hello world\n');
  }).listen(8443);
});

More examples and use cases can be found on GitHub repo.

Security

What if *.backloop.dev DNS A and AAAA entries are not pointing anymore to 127.0.0.1 and ::1 but to another IP (malicious ones)? Then your HTTPS requests will not end-up on your machine, but on this malicious servers.

Even, if this is very unlikely to happend, you may want to be on the safe side by adding <what you need>.backloop.dev in your /etc/hosts file.

127.0.0.1 localhost whatever.backloop.dev ... 
::1 localhost whatever.backloop.dev ... 

Want to contribute?

You need it for python ? php ? react ? You have a idea of a use case.

Feel free to contribute

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment