Certificate Depot

Guides

Home › Guides › Localhost development

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.

Need a self-signed certificate? Use our free generator — browser-compatible SANs, RSA or ECDSA, ZIP or PFX. No signup, no ads, keys never stored.

Further Reading