DEV Community

Emily Flias
Emily Flias

Posted on • Edited on

How can I set up SSL on https://localhost/ [Updated 2026]

Before we start, it's important to note that SSL certificates are typically issued for domain names, not IP addresses or localhost.
To serve https://localhost/ in a way that modern browsers accept (no red warning), you need two things:

  1. A certificate whose Subject Alternative Name (SAN) includes localhost (and often 127.0.0.1 and ::1).
  2. A way for your OS/browser to trust the issuing CA (or the certificate itself).

The most reliable developer workflow is to create a local, trusted CA and issue a cert for localhost.


Option A (recommended): mkcert (trusted HTTPS with minimal friction)

mkcert creates a local certificate authority (CA), installs it into your system trust store, then issues certs for your local domains.

1) Install mkcert

  • macOS (Homebrew): brew install mkcert
  • Windows (Chocolatey): choco install mkcert
  • Windows (Scoop): scoop bucket add extras && scoop install mkcert
  • Linux: install from your distro packages or the project releases; also ensure nss tooling is installed for Firefox trust integration (varies by distro).

2) Create and trust a local CA

mkcert -install
Enter fullscreen mode Exit fullscreen mode

3) Generate a cert for localhost

Run this where you want the files:

mkcert localhost 127.0.0.1 ::1
Enter fullscreen mode Exit fullscreen mode

This produces files like:

  • localhost+2.pem (certificate)
  • localhost+2-key.pem (private key)

4) Configure your local server to use the cert

Node.js (Express or plain HTTPS)

import https from "https";
import fs from "fs";
import app from "./app.js"; // your Express app

https.createServer(
  {
    key: fs.readFileSync("./localhost+2-key.pem"),
    cert: fs.readFileSync("./localhost+2.pem"),
  },
  app
).listen(443, () => console.log("HTTPS on https://localhost"));
Enter fullscreen mode Exit fullscreen mode

If you don’t want root/admin privileges for port 443, use 8443 and browse https://localhost:8443/.

Python (Flask)

from flask import Flask
app = Flask(__name__)

@app.get("/")
def hello():
    return "hello"

if __name__ == "__main__":
    app.run(ssl_context=("localhost+2.pem", "localhost+2-key.pem"), port=8443)
Enter fullscreen mode Exit fullscreen mode

Uvicorn / FastAPI

uvicorn main:app --host 127.0.0.1 --port 8443 \
  --ssl-keyfile localhost+2-key.pem --ssl-certfile localhost+2.pem
Enter fullscreen mode Exit fullscreen mode

Nginx (reverse proxy)

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://127.0.0.1:3000;
  }
}
Enter fullscreen mode Exit fullscreen mode

Option B: Caddy (very convenient local HTTPS)

Caddy can generate and manage a local CA automatically and serve HTTPS locally with minimal configuration.

Example Caddyfile:

localhost {
  reverse_proxy 127.0.0.1:3000
}
Enter fullscreen mode Exit fullscreen mode

Run Caddy and it will handle certs for you (you may need to trust Caddy’s local CA depending on platform prompts).


Option C: OpenSSL self-signed cert (works, but browsers will warn)

This is quick, but you’ll typically see a browser warning unless you manually trust the cert/CA.

Modern browsers require SAN, so use an OpenSSL config:

  1. Create localhost.cnf:
[req]
default_bits = 2048
prompt = no
default_md = sha256
x509_extensions = v3_req
distinguished_name = dn

[dn]
CN = localhost

[v3_req]
subjectAltName = @alt_names

[alt_names]
DNS.1 = localhost
IP.1 = 127.0.0.1
IP.2 = ::1
Enter fullscreen mode Exit fullscreen mode
  1. Generate cert + key:
openssl req -x509 -nodes -days 825 -newkey rsa:2048 \
  -keyout localhost.key -out localhost.crt -config localhost.cnf
Enter fullscreen mode Exit fullscreen mode

Then point your server to localhost.crt / localhost.key.

If you want no warnings, you must add trust manually (varies by OS/browser) or create a local CA and trust it (which is effectively what mkcert automates).


Posted on 02 January 2026

  • “Certificate not valid for this host”: your cert is missing SAN entries for localhost or 127.0.0.1.
  • Chrome/Edge caching/HSTS: if you previously forced HTTPS or hit a bad cert, you may need to clear HSTS for localhost (or use a different hostname like myapp.local).
  • Firefox trust differs: on some systems Firefox uses its own store; mkcert -install usually handles this if the right NSS tooling is present.
  • Port 443 permissions: on macOS/Linux, binding to 443 may require admin/root; use 8443 or a reverse proxy that listens on 443.
  • Containers: if your server runs in Docker, mount the cert/key into the container and reference those paths; trust is still on the host/browser side.

Practical recommendation

If your goal is simply “make https://localhost/ work cleanly in a browser,” use mkcert and run your dev server on 8443 (or use a reverse proxy to 443). It’s the fastest path to a trusted cert with correct SANs.

If you tell me what stack you’re using (Node/Express, Vite, Next.js, Django, Nginx, IIS, Docker, etc.), I can give the exact config snippet for that specific server.

Top comments (0)