DEV Community

Dima Stopel
Dima Stopel

Posted on • Originally published at cert-depot.com

Self-Signed Certificates for localhost Development

Originally published on cert-depot.com. Free, open-source self-signed certificate generator — no signup, keys never stored.

Self-Signed Certificates for localhost Development

Stop clicking past browser warnings. Get a proper green-padlock HTTPS localhost in a few minutes.

Most modern web APIs require HTTPS: Service Workers, Web Crypto, geolocation, camera access, OAuth flows, HTTP/2. Developing locally against HTTP hits constant "feature unavailable" errors. A self-signed cert for localhost fixes this — but only if you trust it properly.

Step 1: Generate the certificate

With cert-depot.com: enter localhost as the Common Name, add 127.0.0.1 and ::1 as IP SANs, generate.

With OpenSSL:

openssl req -x509 -newkey rsa:2048 -nodes \
  -keyout localhost.key -out localhost.crt \
  -days 365 \
  -subj "/CN=localhost" \
  -addext "subjectAltName=DNS:localhost,IP:127.0.0.1,IP:::1"
Enter fullscreen mode Exit fullscreen mode

Step 2: Trust it in your OS

This is the step that makes the browser warning go away. Without it, you still get HTTPS (encrypted traffic), but the browser shows "Not Secure" and some APIs stay blocked.

Step 3: Serve it from your dev server

Vite

// vite.config.js
import fs from "node:fs";

export default {
    server: {
        https: {
            key: fs.readFileSync("localhost.key"),
            cert: fs.readFileSync("localhost.crt"),
        },
    },
};
Enter fullscreen mode Exit fullscreen mode

Next.js (via custom server)

// server.js
const { createServer } = require("https");
const fs = require("fs");
const next = require("next");

const app = next({ dev: true });
const handle = app.getRequestHandler();

app.prepare().then(() => {
    createServer({
        key: fs.readFileSync("localhost.key"),
        cert: fs.readFileSync("localhost.crt"),
    }, handle).listen(3000);
});
Enter fullscreen mode Exit fullscreen mode

webpack-dev-server

// webpack.config.js
module.exports = {
    devServer: {
        server: {
            type: "https",
            options: {
                key: "./localhost.key",
                cert: "./localhost.crt",
            },
        },
    },
};
Enter fullscreen mode Exit fullscreen mode

Python (http.server replacement)

from http.server import HTTPServer, SimpleHTTPRequestHandler
import ssl

ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ctx.load_cert_chain("localhost.crt", "localhost.key")

server = HTTPServer(("localhost", 4443), SimpleHTTPRequestHandler)
server.socket = ctx.wrap_socket(server.socket, server_side=True)
server.serve_forever()
Enter fullscreen mode Exit fullscreen mode

Alternative: mkcert

mkcert automates the trust step by installing a local CA, then issuing certs from that CA. Install with:

brew install mkcert  # macOS
mkcert -install
mkcert localhost 127.0.0.1 ::1
Enter fullscreen mode Exit fullscreen mode

It's convenient. The tradeoff is that mkcert installs its own root CA on your machine — meaning anyone who gets the mkcert CA private key could issue trusted certs for any domain. For a personal laptop this is fine; for shared machines or CI, prefer explicit per-cert trust.

Why not use a real cert for localhost?

You can't. Let's Encrypt (and every other publicly-trusted CA) refuses to issue certs for localhost, private IP ranges, or any domain they can't validate from the public internet. Self-signed is the only option.

Further Reading

Top comments (0)