How to Create Your Own SSL Certificate Authority for Local HTTPS Development


In my last article I described how to generate your own self-signed SSL certificates and add them to macOS Keychain so that your browser doesn’t give you a privacy error.

Screenshot of Chrome browser with security warning

Soon after it was published, Ross McKay made a very interesting comment on that article:

If you have a few servers you need to do this with, you can just create yourself a CA (Certifying Authority) certificate and load that instead. Then your self-signed certs, signed by your CA cert, will all be accepted without you needing to load each one.

Bill Nye mind blown

So basically he’s saying that I can be a certificate authority (CA) like Let’s Encrypt, Amazon, Verisign, Comodo, etc but just for my local network. How did I not know about that? So cool. But how does it work exactly?

How It Works

After some research I think I get it now. To request a certificate from a CA like Verisign, you send them a Certificate Signing Request (CSR), and they give you a certificate in return that they signed using their root certificate and private key. All browsers have a copy (or access a copy from the operating system) of Verisign’s root certificate, so the browser can verify that your certificate was signed by a trusted CA.

That’s why when you generate a self-signed certificate the browser doesn’t trust it. It’s self-signed. It hasn’t been signed by a CA. But as Ross pointed out, we can generate our own root certificate and private key, add the root certificate to all the devices we own just once, and then all certificates that we generate and sign will be inherently trusted.

Becoming a (tiny) Certificate Authority

It’s kind of ridiculous how easy it is to generate the files needed to become a certificate authority. It only takes two commands. First, we generate our private key:

openssl genrsa -des3 -out myCA.key 2048

You will be prompted for a pass phrase, which I recommend not skipping and keeping safe. The pass phrase will prevent anyone who gets your private key from generating a root certificate of their own. Output should look like this:

Generating RSA private key, 2048 bit long modulus
e is 65537 (0x10001)
Enter pass phrase for myCA.key:
Verifying - Enter pass phrase for myCA.key:

Then we generate a root certificate:

openssl req -x509 -new -nodes -key myCA.key -sha256 -days 1825 -out myCA.pem

You will prompted for the pass phrase of your private key (that you just choose) and a bunch of questions. The answers to those questions aren’t that important. They show up when looking at the certificate, which you will almost never do. I suggest making the Common Name something that you’ll recognize as your root certificate in a list of other certificates. That’s really the only thing that matters.

Enter pass phrase for myCA.key:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
Country Name (2 letter code) [AU]:CA
State or Province Name (full name) [Some-State]:Nova Scotia
Locality Name (eg, city) []:Truro
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Delicious Brains Inc
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:Delicious Brains
Email Address []

You should now have two files: myCA.key (your private key) and myCA.pem (your root certificate).

🎉 Congratulations, you’re now a CA. Sort of.

To become a real CA, you need to get your root certificate on all the devices in the world. Let’s start with the ones you own.

Installing Your Root Certificate

We need to add the root certificate to any laptops, desktops, tablets, and phones that will be accessing your HTTPS sites. This can be a bit of a pain, but the good news is that we only have to do it once. Once our root certificate is on each device, it will be good until it expires.

Adding the Root Certificate to macOS Keychain

  1. Open the macOS Keychain app
  2. Go to File > Import Items…
  3. Select your private key file (i.e. myCA.pem)
  4. Search for whatever you answered as the Common Name name above
  5. Double click on your root certificate in the list
  6. Expand the Trust section
  7. Change the When using this certificate: select box to “Always Trust”
  8. Close the certificate window
  9. It will ask you to enter your password (or scan your finger), do that
  10. 🎉 Celebrate!

Creating CA-Signed Certificates for Your Dev Sites

Now that we’re a CA on all our devices, we can sign certificates for any new dev sites that need HTTPS. First, we create a private key:

openssl genrsa -out 2048

Then we create a CSR:

openssl req -new -key -out

You’ll get all the same questions as you did above and, again, your answers don’t matter. In fact, they matter even less because you won’t be looking at this certificate in a list next to others.

You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
Country Name (2 letter code) [AU]:CA
State or Province Name (full name) [Some-State]:Nova Scotia
Locality Name (eg, city) []:Truro
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Delicious Brains Inc
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:Mergebot
Email Address []

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

Next we’ll create the certificate using our CSR, the CA private key, the CA certificate, and a config file, but first we need to create that config file.

The config file is needed to define the Subject Alternative Name (SAN) extension we discussed in my last article. I created a new file named and added the following contents:

keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names

DNS.1 =
DNS.2 =

We’re using a different config file for the SAN than in my last article because we’ll be running the openssl x509 command instead of the openssl req command. From what I understand, the x509 command is needed to do the signing with the root certificate and private key. Again, I found this example config file on Stack Overflow and it seems to work.

Now we run the command to create the certificate:

openssl x509 -req -in -CA myCA.pem -CAkey myCA.key -CAcreateserial \
-out -days 1825 -sha256 -extfile

You should be prompted for your CA private key pass phrase.

I now have three files: (the private key), (the certificate signing request), and (the signed certificate).

I can now configure my web server with the private key and the certificate. If you’re running a Linux server, you can use the instructions in our Hosting WordPress Yourself series. If you’re using MAMP, you can select the certificate and key files using the UI:

MAMP application screenshot of SSL configuration panel

For any other dev sites, we can just repeat this last part of creating a certificate. No need to install any new certificates on any of my devices. Much better.


After going through the process of setting up my own CA and signing certificates with it, there’s no question this is a better way to go than creating individual self-signed certificates as described in my last article. The fact that you only have to setup the CA and add it to your devices once means you save a bunch of time for each new certificate you create in the future. It’s a no-brainer to go this route.

Have you tried setting up a CA of your own? Maybe you’ve been using your own CA for years? Are there any drawbacks that I missed? Let me know in the comments below.

About the Author

Brad Touesnard

As founder of Delicious Brains Inc., Brad wears many hats; from coding and design, to marketing and partnerships. Before starting Delicious Brains, Brad was a busy freelance web developer, specializing in front-end development.

  • Philip Newcomer

    Firefox doesn’t use the macOS keychain (it maintains its own certificate store), so any certificates you add to the Keychain won’t be recognized by Firefox. In order for the CA-signed certificates to be recognized by Firefox you’ll need to go into the Firefox settings and manually add the root certificate there.

    • Henrique Mattos

      Philip, thanks for the information. I’ve not been struggling with this for weeks because I eventually gave up and ended up using Chrome for corporate websites that needs SSO. But now with this clue, I will digg more into having the CA-signed into Firefox. If you happen to have an easy, step-by-step tutorial on how to add those to FF (I’m using DevEd), I would appreciate.

      Anyway, already grateful. 🙂

      • Dairo Barrios

        copy-paste in your firefox url about:preferences#privacy or maybe in preferences and then privacity and security,option certificades ,view certificades,option autorities and then import your root certificade with extension .pem ej: myCA.pem

        • Henrique Mattos

          Already tried that, but thanks! 🙂

  • Jean-Luc GARNIER

    Hi Brad,

    How can I “translate” this into the Windows world? Say, using Chrome on Win10…
    Thanks in advance for any advice!

    • Sallie Goetsch

      That would be my question, too. There are actually WordPress developers who don’t use Macs.

    • Henrique Mattos

      Basically the command-line would be the same if you have a Git Bash or other Unix-like CLI integrated to your CMD/PowerShell. And then you’d import the CA-signed to Chrome in a regular way, since Win10 doesn’t have a Keychain to store those.

  • Mary-Ann Zykin

    Hey Brad,

    Thanks so much for writing this. I ran into an issue with geolocation on a local build and needed to install an SSL certificate, and just so happened to get an email with this article on the same day. Great stuff!

    I did run into an issue when following along. My issue was creating the config file, which I think you could have been a little bit more clear about. I would include the full text of your config file within this article since I was confused about what I had to add or change.

    The other issue was this code snippet:
    openssl x509 -req -in -CA myCA.pem -CAkey myCA.key -CAcreateserial
    -out -days 1825 -sha256 -extfile
    My issue was that the .ext at the end of your command should have been “.config” (or in my case, I just made it .cnf) It took a second to figure out but wasn’t immediately clear.

    However, even after successfully creating the certificate, Google was just not having it. It was giving me the error “ERR_CERT_COMMON_NAME_INVALID” and when I looked at the details, it said that I was missingSubjAltName (or something along those lines). After digging around some other articles that explained how to create a self-signed certificate, I noticed there was one little piece missing from the command: -extensions x509_ext after -sha256. After I added that little piece (and changed .ext to .cnf), I was able to successfully create the certificate, add it to MAMP, and was good to go!

    The final code was:
    openssl x509 -req -in -CA myCA.pem -CAkey myCA.key -CAcreateserial -out -days 1825 -sha256 -extensions x509_ext -extfile

    I can also confirm that this doesn’t work for Firefox right out of the gate.

    Thanks again for teaching me something new! I always look forward to y’all’s articles and walkthroughs.

    • pbtura

      Thank you! I was pulling my hair out trying to figure out what I missed. Adding that -extensions did the trick.

  • Anton VS

    Is there any reason to set up an SSL certificate / HTTPS for local development?

    • Henrique Mattos

      Developers have been editing computer hosts file to redirect the original domain (say to localhost (say so they can use the fully qualified URI/URL in the development. It’s a good way to develop WordPress themes and plugins and then upload those to the production webserver not needing to script into the DB to rewrite permalinks, attachment URLs, etc…

      Also, having HTTPS is mandatory for some WooCommerce plugins or some XSS integration and therefore it’s nice to have it in your dev environment. 🙂

  • Jano Hernández Cobos

    Great tutorial.

    Please note this is not valid for IIS servers, it is needed to generate a pxf file and add a intermediate certificate (and you don’t have it)

  • claudia4152156

    For developed the HTTPS there are more people are have more interest and i hope they found good tricks and tips from here. To get success such will be so more better for them.

  • Andy Waller

    I followed the directions up until the last step. I can’t figure out how to configure the web server with the private key and certificate. I’m using the free version of DesktopServer, and there’s no UI like there is for MAMP.

    How do I do this?

  • Laukik Patel

    Thank you so much @bradt

  • numediaweb

    Thanks so much! I turned this into an Ansible role which allows me to generate unlimited hosts with each one a unique cert!

  • gmannz

    Thanks Brad, this was a good concise article and worked well. I secured my WIFI AirOS nano WIFI AP’s with a new certificate, as well for my lab I will be applying these to some other devices. Keep up the good work. Greg

  • hutchings7346

    We are so happy to get more update HTTPS Development and most of the people are like to get this one. So i hope day by day it will be so more usable for us.

  • Clément Guinet

    Thanks for this very useful post 🙂

  • Genius! I used this tutorial to help with local Traefik & docker. Thanks for making it rather easy to follow.

  • Boboss

    Thanks for the guide,
    Maybe should you update the max lifetime days to 825

  • I created a little bash script to quickly create the certificate against the CA for a domain:

  • Dev

    Is it possible to issue a Wildcard? I’ve tried setting common name as * but I get ERR_CERT_COMMON_NAME_INVALID from chrome. BTW many thanks for the useful article!