BeepBoopIT

Complete Guide: Setting Up Local HTTPS Servers with mkcert and Puma, Express, or Gunicorn

author picture

·03/08/2024·4 min read

In this article I'm going to demonstrate hassle-free ways to setup local development HTTPS servers for a few popular web servers using mkcert.

Before we get into the process I'll just answer a few important questions you may have:

Question Answer
Why mkcert when I can generate the SSL certificates myself? You can totally generate and sign your own certificates yourself! We are using mkcert in this tutorial as it is a really quick and easy and effective way to get development certificates. If you choose to manually setup your certificates then you can skip to How to Configure the WebServer to Use Locally Signed Certificates.
My web server isn't covered in this article, can I still follow along? Absolutely, you can follow this guide to generate the certificates and then lookup the documentation for your the server configuration you need.
Can I use this method to setup an HTTPS server for production or staging environments? No, it is not recommended to follow this guide for production or staging environments and should only be used for serving local development environments. There are other free, easy to use tools, such as Certbot, which are much better suited for production environments.
Why do I need HTTPS locally at all? If you run production in HTTPS then also running HTTPS locally can be beneficial for a number of cases, such as for testing third-party libraries requiring HTTPS or for debugging mixed content errors. Ultimately, while you dont need to use HTTPS for development in most cases, having a development environment that more closely resembles the production environment is never a bad thing.

Getting Local Certificates with mkcert

Installing mkcert

Instructions for installing mkcert on MacOS, Linux or Windows can be found directly on the mkcert github repo here.

Generating the certificates

First we need to install the local CA in the system trust store:

mkcert -install

Now we're ready to generate the certificates themselves:

mkcert <domain>

Just substitute the tag in in the following snippet to whatever domain(s) you are planning on using for development, here are a few examples:

# Certificate for localhost:
   mkcert localhost 

   # certificate for example.com, dev.example.org and ::1:
   mkcert example.com dev.example.org ::1

   # wildcard certificate:
   mkcert *.my-cool-app.com

And just like that we have self-signed certificates generated! Just make sure to take note of both the certificate and key file locations which will be included in the output from mkcert like this: imageedit_3_9214871462.jpg

Configuring a Web Server to Use mkcert

Puma (Ruby on Rails)

To start Puma using the local certificates, you can use the "-b" (bind) flag with a constructed URI like this:

rails s puma -b "ssl://127.0.0.1:3000?key=<path_to_key_file>&cert=<path_to_cert_file>"

Or, to start Puma with these parameters every time, add this block to the bottom of the Puma configuration file:

if ENV['RACK_ENV'] == 'development'

  key_file = "#{File,join(<path_to_key_file>)}" 
  cert_file = "#{File.join(<path_to_cert_file>)}"

  ssl_bind '127.0.0.1', 3000, {
    key: key_file,
    cert: cert_file
  }
end

And that's it! You now have Puma serving over HTTPS.

If you want to look more into configuring puma with the ssl_bind option, click here.

Express (Node.js)

To get HTTPS setup locally with Express a few things need to be changed and added to the configuration file where the Express.js app instance is instantiated (usually something like index.js):

First make sure to require the 'https' module instead of 'http':

const https = require('https');

Next configure the certificate options for the HTTPS server using the paths to the local certificates you got from mkcert:
const fs = require('fs')

const options = {
  key: fs.readFileSync(<path_to_key_file>),
  cert: fs.readFileSyn(<path_to_cert_file>)
};

Now create the app and HTTPS server:
const app = express();

const server = https.createServer(options, app);

And that's it! Express is now setup to use HTTPS, you can just start listening for requests on whatever port you want as you normally would.

For more information on HTTPS options with Node.js/Express check out the documentation here

Gunicorn

Setting up HTTPS on Gunicorn is super simple! We just need to add a couple flags to the Gunicorn start command:

gunicorn --keyfile=<path_to_key_file> --certfile=<path_to_cert_file>  test:app

These flags can also just be included in your Gunicorn config file if you are using one.

And that's it! You are now using HTTPS with Gunicorn. For more information on how to configure Gunicorn check out the documentation here.

author picture

Harrison Peters

I am a full-stack developer who started their coding journey with Java almost 10 years ago. I hold a Bachelor of Science in Computing Science from the University of Alberta, and have co-authored 9 state-of-the-art web servers and databases that are published in academic journals. Nowadays my favourite languages are Ruby, Javascript, and Rust.
Outside of coding I enjoy Skiing & Snowboarding, camping and playing video games.