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"
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"),
},
},
};
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);
});
webpack-dev-server
// webpack.config.js
module.exports = {
devServer: {
server: {
type: "https",
options: {
key: "./localhost.key",
cert: "./localhost.crt",
},
},
},
};
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()
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
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.