Qvantor logo QvantorInfrastructure, explained plainly
Dev Tools

Local HTTPS Development With mkcert in Under Ten Minutes

Your app works on plain http://localhost:3000, then breaks the moment it touches a secure cookie, a service worker or an OAuth callback that insists on HTTPS.

Developer laptop showing a browser padlock on a local test domain beside a terminal window

Your app works on plain http://localhost:3000, then breaks the moment it touches a secure cookie, a service worker or an OAuth callback that insists on HTTPS. Setting up local HTTPS development with mkcert fixes that in about ten minutes, and you never have to click through a browser warning again.

This guide walks through the setup on macOS, Linux and Windows, shows how to plug the certificate into the dev servers most small teams actually run, and covers the handful of mistakes that make people give up and go back to http.

Why Local HTTPS Development Matters More Than It Used To

A few years ago you could ignore TLS until staging. That is no longer true. Browsers treat a growing list of features as secure-context only: service workers, the clipboard API, geolocation, WebAuthn passkeys and cookies marked Secure with SameSite=None. If your production site depends on any of them, testing over plain http means you are testing a different app.

Localhost gets some special treatment, so a few of these features work on http://localhost. They stop working the moment you use a custom hostname like app.test or point a phone on your Wi-Fi at 192.168.1.40. That is exactly when most people discover the problem, usually at 6 pm the day before a demo.

The other common pain is third-party login. Providers increasingly reject non-HTTPS redirect URIs, or accept them only for the literal string localhost. If your app runs on multiple subdomains locally, you need real certificates.

What mkcert Actually Does

mkcert creates a small private certificate authority on your machine and adds it to your system trust store, plus the Firefox and Java stores if it finds them. It then signs certificates for whatever hostnames you ask for. Your browser trusts those certificates because it trusts the local CA.

Nothing leaves your machine. There is no account, no renewal cron job and no DNS challenge. Certificates it issues are valid for a little over two years, so you set this up once per laptop and forget it.

Setting Up mkcert Step by Step

  1. Install it. On macOS use Homebrew: brew install mkcert, plus brew install nss if you use Firefox. On Debian or Ubuntu install libnss3-tools first, then grab the mkcert binary from your package manager or the release page. On Windows, choco install mkcert or scoop install mkcert both work.
  2. Create and trust the local CA. Run mkcert -install. You will get a password prompt on macOS and a confirmation dialog on Windows. This is the only step that needs elevated rights.
  3. Issue a certificate. From your project folder, run mkcert app.test "*.app.test" localhost 127.0.0.1 ::1. You get two files, something like app.test+4.pem and app.test+4-key.pem. Rename them to cert.pem and key.pem to keep configs readable.
  4. Point the hostname at your machine. Add 127.0.0.1 app.test api.app.test to your hosts file. Wildcards do not work in the hosts file, so list each subdomain you use.
  5. Restart the browser. Chrome and Edge usually pick up the new CA immediately. Firefox needs a full restart.

On a typical laptop the whole sequence takes three to five minutes. If you want new teammates to repeat it without reading a wiki page, wrap steps 3 and 4 in a target, as shown in this guide on setting up a Makefile task runner for small projects.

Wiring the Certificate Into Your Dev Server

Most dev servers accept a cert and key path directly. A few examples that cover a lot of small projects:

ToolWhere the cert goesNotes
Viteserver.https with key and cert read from diskSet server.host to app.test if you use a custom name
Next.jsthe experimental HTTPS flag on next dev, plus key and cert flagsIt can generate its own cert too, but sharing one mkcert cert keeps things consistent
Djangorunserver_plus from django-extensions, with its cert file optionPlain runserver has no TLS support
Caddy or nginxtls cert.pem key.pem in the site blockUseful when several services sit behind one local proxy

If you run three or more services locally, the proxy route is usually cleaner. One Caddy instance terminates TLS for app.test, api.app.test and auth.app.test, and each service keeps listening on plain http on its own port. Your app code never touches a certificate.

The Advice to Commit Certificates Is Wrong

You will see blog posts and starter templates that commit cert.pem and key.pem to the repo so new developers "just get HTTPS". Do not do this. The key is only harmless if every developer's machine trusts a different CA, and committed certs only work on the machine that generated them anyway. On every other laptop the browser shows a warning, so the convenience is imaginary.

Worse, the rootCA-key.pem file that mkcert creates is genuinely sensitive. Anyone who has it can mint certificates your browser trusts for any domain, including your bank. It lives in the folder shown by mkcert -CAROOT, and it should never be copied, synced to cloud storage or shared on a team drive.

The better pattern: add *.pem to .gitignore, and commit a script that generates certs on first run. Each developer gets their own CA and their own leaf certificate. It is a little slower on day one and far safer for the next two years. If you already use hooks, a check in your pre-commit hooks for small teams can block any staged .pem file before it reaches a commit.

Common Problems and Quick Fixes

  • Browser still says "not secure". The hostname you typed is not in the certificate. Run openssl x509 -in cert.pem -noout -text and check the Subject Alternative Name list.
  • Node rejects the cert in server-side fetch calls. Node uses its own CA bundle. Set NODE_EXTRA_CA_CERTS to the rootCA.pem path from mkcert -CAROOT.
  • Docker containers do not trust it. Mount rootCA.pem into the container and run update-ca-certificates in the image, or call the service over plain http inside the Docker network.
  • Testing on a phone. Install rootCA.pem on the device as a trusted profile. On iOS you also have to enable full trust under Certificate Trust Settings, which most people miss.

Mismatched setups between laptops are one of the classic sources of "works on my machine" bugs, so it helps to read about local development environment drift before rolling this out to a whole team.

FAQ

Is mkcert safe to use on a work laptop?

Yes, as long as you treat the CA key as a secret. The risk is not the certificates themselves but the root key in the CAROOT folder. Keep it off shared drives and backups that other people can read.

Can I use Let's Encrypt for local HTTPS development instead?

Only if your dev hostname is a real public domain you control and you can complete a DNS challenge. For most local work that is extra setup with no benefit. mkcert certificates are trusted only on your machine, which is exactly what you want.

Do I need HTTPS for localhost at all?

Not always. Browsers treat http://localhost as a secure context for many APIs. You need real certificates once you use custom hostnames, test on other devices, or work with login providers and cookies that demand HTTPS.

How do I remove mkcert later?

Run mkcert -uninstall to remove the CA from your trust stores, then delete the CAROOT folder. Any certificates it issued stop being trusted immediately.

ON
Obi Nwachukwu

Obi works on account security and explains phishing, passwords and access in plain terms.

More posts by Obi

More in Dev Tools

01doorganico 084goad 3hot8 4465456 after5internprogram ahandfulofblessings ahmadpc ahomcybersecurity allodialresources annodex antiquesandperiodcollectibles aotc asianworldkashmir beedlectrics bloggertemplatefree bluefeatheragency bodyweightsolution campaign08blog carfixadvisor catstreetcollective cbstvsports cnfanspurchasing committeeof500yearsofdignityandresistance covertopsdjs cs-kaiyuangame cuestacomunicaciontotal cxoutlet dancebetgame deskgearhub destinyvillastay detectmagazine di33 diamondsteelbuildings donmo dyanadropship emarsysfinance emimattl escapechan eskitarih evautoplay everythingweddings-orlando farmzone feedviewerapp foxstudiosscratch gamevui123 gleasongrade greetingsday hacklabasu haskell iw-kaiyun jagoanproperti jilidskripsi joltbusinessfinance juegodepokergratis juegospucca kaeru-festival latestmehndidesigns lestanzediottaviano litemagazines localcommunityhero lustrsazan mariashebets menswheels newwebstechs1 nikkit ootheday oxigenvpn plurfitness prefetial probudgetcarpetcleaning psychicmrsmartin pyaten rejoignezlogis rexzbot riselayouts ruchin rwxqsgd5 sakaltime shibiaowang shop-bot skrtips strcoding tetapatrium topselller uneplanteartificielle vipertechcr webtech1223411 wo-kygaming yamaha-mekar yeknava yoybuysheets zenunwa yamaha-mekar jilidskripsi ankertoto ankertoto ankertoto ankertoto