SSL on Localhost — HTTPS for Development

Most development happens over plain HTTP on localhost. But sometimes you need HTTPS locally: testing service workers (which require HTTPS), working with OAuth callbacks that demand HTTPS redirect URLs, testing mixed-content scenarios, using HTTP/2, or APIs that refuse non-HTTPS requests. The challenge is getting a certificate that your browser trusts without those annoying security warnings.

The Tool: mkcert

mkcert is the easiest way to get trusted HTTPS on localhost. It creates a local Certificate Authority (CA), installs it in your system's trust store, and generates certificates signed by that CA. Your browser trusts them automatically — no warnings, no clicking through scary pages.

Install mkcert

# Mac
brew install mkcert
brew install nss  # Required for Firefox support

# Windows (with Chocolatey)
choco install mkcert

# Windows (with Scoop)
scoop install mkcert

# Linux
sudo apt install libnss3-tools
# Then download from https://github.com/FiloSottile/mkcert/releases

Set Up the Local CA

# Install the local CA in your system trust store (one-time setup)
mkcert -install

# You'll see: "The local CA is now installed in the system trust store!"

Generate Certificates

# Generate cert for localhost
mkcert localhost 127.0.0.1 ::1

# Creates two files:
#   localhost+2.pem       (certificate)
#   localhost+2-key.pem   (private key)

# For custom domains too
mkcert localhost 127.0.0.1 myapp.local api.local

Use with Dev Servers

Vite (Port 5173)

// vite.config.js
import fs from 'fs';

export default {
    server: {
        https: {
            key: fs.readFileSync('./localhost+2-key.pem'),
            cert: fs.readFileSync('./localhost+2.pem'),
        }
    }
}
// → https://localhost:5173

Next.js (Port 3000)

// Create a custom server.js or use the experimental flag:
// package.json
{
    "scripts": {
        "dev": "next dev --experimental-https"
    }
}
// Next.js 13.5+ generates its own self-signed cert automatically

Express.js (Node)

const https = require('https');
const fs = require('fs');
const express = require('express');

const app = express();

const options = {
    key: fs.readFileSync('localhost+2-key.pem'),
    cert: fs.readFileSync('localhost+2.pem')
};

https.createServer(options, app).listen(3000, () => {
    console.log('HTTPS server running on https://localhost:3000');
});

Nginx

# /etc/nginx/sites-available/localhost-ssl
server {
    listen 443 ssl;
    server_name localhost;

    ssl_certificate     /path/to/localhost+2.pem;
    ssl_certificate_key /path/to/localhost+2-key.pem;

    location / {
        proxy_pass http://localhost:3000;
        proxy_set_header Host $host;
    }
}

Apache

<VirtualHost *:443>
    ServerName localhost
    SSLEngine on
    SSLCertificateFile /path/to/localhost+2.pem
    SSLCertificateKeyFile /path/to/localhost+2-key.pem
    DocumentRoot /var/www/html
</VirtualHost>

When You Need HTTPS Locally

ScenarioWhy HTTPS Required
Service Workers / PWABrowsers only register service workers on HTTPS (or localhost plain HTTP as an exception, but not always reliable)
OAuth / Social LoginMany OAuth providers (Google, Facebook) require HTTPS callback URLs, even in development
Secure CookiesCookies with Secure flag only sent over HTTPS
Mixed Content TestingNeed to verify your site works fully on HTTPS before production
HTTP/2Browsers only support HTTP/2 over HTTPS
WebRTCCamera/microphone access requires HTTPS (or localhost)
CORS with credentialsSome auth flows require matching protocol between frontend and backend

Alternatives to mkcert

ToolProsCons
mkcertSimple, trusted by browser, one commandMust install on each machine
openssl (self-signed)Available everywhere, no installBrowser shows security warning every time
Vite --httpsZero config for Vite usersSelf-signed, shows warning
Caddy (reverse proxy)Automatic HTTPS, including localAdds another tool to your stack

Troubleshooting

Browser still shows "Not Secure": Run mkcert -install again. Close and reopen the browser. For Firefox, you may need the nss package installed before running mkcert -install.

"NET::ERR_CERT_AUTHORITY_INVALID": The local CA isn't trusted. This happens if you generated certs on one machine and copied them to another. Run mkcert -install on each machine that needs to trust the certificates.

Port conflict with port 443: Port 443 requires admin/root privileges. Use a higher port like 8443 for development, or run your dev server behind a reverse proxy.