Why You Should Generate Self Signed Certificate for Local Development (and When to Stop)

Why You Should Generate Self Signed Certificate for Local Development (and When to Stop)

Encryption isn't optional anymore. You've probably seen that annoying "Not Secure" warning in Chrome or Firefox while trying to test a local project. It’s a mood killer. Usually, we want our local dev environment to mirror production as closely as possible, which means running over HTTPS. But getting a "real" certificate from a CA like Let’s Encrypt for a machine that isn't even on the public internet? That is a massive headache.

So, we do what any sane developer does. We generate self signed certificate files.

Basically, a self-signed certificate is an identity document where you are both the person being identified and the notary signing the paper. It says, "I am who I say I am, because I said so." Browsers hate this. They don't trust you. But for your private lab, your home server, or your Docker containers, it’s often the only way to get TLS up and running without paying a dime or jumping through DNS validation hoops.

The OpenSSL Way: Dirty, Fast, and Effective

Most people go straight for OpenSSL. It's the industry standard, even if the command-line arguments feel like they were written by someone who enjoys pain. If you're on Linux or macOS, you likely already have it. Windows users usually grab it via Git Bash or Chocolatey.

To generate self signed certificate assets that actually work with modern browsers, you can't just run a basic one-liner anymore. Chrome, specifically, started demanding a field called Subject Alternative Name (SAN) a few years ago. If you just provide a Common Name (CN), the browser will still throw a "Subject Alternative Name Missing" error. It’s frustrating.

Here is the standard "modern" way to do it in one go:

openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 365 -nodes -subj "/C=US/ST=State/L=City/O=Organization/OU=Unit/CN=localhost" -addext "subjectAltName=DNS:localhost"

Let's break that down because it's a lot. The -x509 part tells OpenSSL we want a self-signed cert, not a request for one. The -nodes flag is actually "no DES," meaning it won't encrypt the private key with a password. If you're putting this in an automated script, you definitely want -nodes, or your web server will hang on startup asking for a password you can't type. 4096 bits for the RSA key is the current sweet spot for security vs. performance.

Honestly, 2048 is fine for local stuff, but why not go bigger?

Why Your Browser Still Screams at You

You generated the files. You pointed Nginx or Apache to them. You refreshed the page. And... it's still red. "Your connection is not private."

This happens because your computer’s Trust Store has no idea who you are. To fix this, you have to manually import your cert.pem (or .crt) into your OS or browser’s trusted root authorities. On macOS, you drag it into Keychain Access and set it to "Always Trust." On Windows, you use certmgr.msc.

It’s a manual, annoying chore. If you have ten developers on a team, everyone has to do this. This is why many teams are moving away from raw OpenSSL for internal tools and toward something like mkcert.

Filippo Valsorda, a well-known cryptography engineer, created mkcert specifically because generating these certificates manually is a repetitive nightmare. It creates its own local Certificate Authority (CA) on your machine. Once you trust that one CA, every certificate you generate through the tool is automatically trusted by your local browsers. It’s magic, honestly. You just run mkcert -install and then mkcert localhost. No more red screens.

Security Risks: Don't Do This in Public

We need to be super clear about one thing: never, ever use a self-signed certificate on a public-facing website.

If you do, you're basically training your users to ignore security warnings. That is a gift to hackers. When a user sees a certificate error on a public site, it usually means a Man-in-the-Middle (MITM) attack is happening. If they're used to seeing your "secure" site throw errors, they’ll click "Advanced -> Proceed" and give away their credentials.

Also, self-signed certificates provide encryption (privacy) but zero validation (identity). Someone could spoof your site, generate their own self-signed cert with the same name, and your users wouldn't know the difference.

For anything public, use Let’s Encrypt. It’s free, it’s automated via Certbot, and it’s trusted by every major entity on earth.

The Weird Edge Cases: IoT and Internal APIs

Sometimes you aren't building a website. Maybe you're working with IoT devices or internal microservices that talk to each other over gRPC. In these cases, you might generate self signed certificate sets for "Mutual TLS" (mTLS).

In mTLS, the server checks the client’s certificate, and the client checks the server’s. It’s a handshake where both parties have to prove who they are. This is incredibly common in Kubernetes environments using service meshes like Istio or Linkerd. They actually rotate these certificates automatically every few hours. You don't even see it happening.

✨ Don't miss: How to SharePlay Apple Music Without the Headache

If you're doing this manually for a small project, remember that certificate expiration will bite you. I once spent six hours debugging a "Connection Refused" error only to realize my self-signed dev cert had expired at midnight. Check your -days flag. Set it to 365 or even 825 (the maximum many browsers will allow now for various security reasons).

Modern Alternatives to Manual Generation

If you’re tired of the command line, there are "Manager" tools.

  1. Caddy Server: This web server is a godsend. It handles TLS automatically. If you run it locally, it will attempt to install its own CA and manage your certificates for you without you ever touching OpenSSL.
  2. Traefik: Similar to Caddy but built for Docker. It can handle the heavy lifting of cert generation in a containerized world.
  3. Step-ca: If you’re at a company and need an internal CA that isn't just one guy's laptop, Smallstep’s step-ca is the professional way to do it. It lets you run your own private Let's Encrypt-style service.

Actionable Next Steps

If you need to get a local site running on HTTPS right now, don't overthink it.

First, check if you have OpenSSL installed by typing openssl version in your terminal. If you do, use the long command I mentioned earlier to generate your .key and .pem files. Make sure your Common Name matches your local URL (like mysite.test or localhost).

Once the files exist, update your web server configuration. For Nginx, that means adding ssl_certificate and ssl_certificate_key paths to your server block. Don't forget to listen on port 443.

Finally—and this is the part everyone skips—actually add the certificate to your system's trust store. If you're on a Mac, open Keychain Access, find your cert, double-click it, and under "Trust," change it to "Always Trust." Restart your browser.

If you find yourself doing this more than once a month, stop. Install mkcert. It will save you hours of configuration fatigue and keep those "Not Secure" warnings from cluttering your workflow. It’s the difference between fighting your tools and actually building your product.