HTTPS Setup for Local Development

Many modern browser features require HTTPS, including:

  • Service Workers
  • Progressive Web Apps (PWA)
  • Web Authentication (WebAuthn/Passkeys)
  • Secure Cookies (SameSite=None; Secure)
  • Camera and Microphone APIs
  • Geolocation APIs
  • Some OAuth and SSO authentication flows

While localhost is treated as a secure context for some browser features, testing with real HTTPS certificates is often necessary.


Multiple Options Available

Option 1: Use OpenSSL to Create a Self-Signed Certificate

Generate Certificate

openssl req -x509 -newkey rsa:4096 \
-keyout key.pem \
-out cert.pem \
-days 365 \
-nodes

This generates:

cert.pem
key.pem

Run http-server with HTTPS

http-server -S -C cert.pem -K key.pem

Browse to:

https://localhost:8080

Expected Browser Warning

You will typically see a warning such as:

Your connection is not private

This is normal because the certificate is self-signed.


mkcert creates locally trusted development certificates and avoids browser security warnings.

Install mkcert

Windows
winget install FiloSottile.mkcert
macOS
brew install mkcert
Linux

See project documentation for distribution-specific packages.

Install Local Certificate Authority

mkcert -install

Generate Certificates

mkcert localhost

Generated files:

localhost.pem
localhost-key.pem

Use with http-server

http-server \
-S \
-C localhost.pem \
-K localhost-key.pem

Browse to:

https://localhost:8080

Benefits:

  • Trusted by the local machine
  • No browser warnings
  • Works with Chrome, Edge, Firefox, and Safari
  • Excellent for testing authentication flows

Option 3: HTTPS with Python

Python's built-in server does not natively support HTTPS from a simple command line option.

Create a file named https_server.py:

from http.server import HTTPServer, SimpleHTTPRequestHandler
import ssl

httpd = HTTPServer(('localhost', 4443), SimpleHTTPRequestHandler)

httpd.socket = ssl.wrap_socket(
    httpd.socket,
    certfile='cert.pem',
    keyfile='key.pem',
    server_side=True
)

httpd.serve_forever()

Run:

python https_server.py

Browse to:

https://localhost:4443

Option 4: HTTPS with Node.js Express

Install Express:

npm install express

Example HTTPS server:

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

const app = express();

app.use(express.static('.'));

https.createServer({
    key: fs.readFileSync('key.pem'),
    cert: fs.readFileSync('cert.pem')
}, app).listen(8443);

Run:

node server.js

Browse to:

https://localhost:8443

Option 5: HTTPS with NGINX in Docker

Mount certificates into the container and configure SSL:

docker run -d \
-p 443:443 \
-v $(pwd)/certs:/etc/nginx/certs \
-v $(pwd)/html:/usr/share/nginx/html \
nginx

This approach closely mirrors production environments.


Common HTTPS Testing Scenarios

Testing Secure Cookies

Example:

Set-Cookie: token=abc123; Secure; SameSite=None

Without HTTPS, browsers often reject these cookies.


Testing OAuth Authentication

Many identity providers require:

https://localhost

instead of:

http://localhost

for redirect URIs.

Examples include:

  • Azure AD / Microsoft Entra ID
  • Okta
  • Auth0
  • Keycloak
  • Google Identity Services

Testing Service Workers

Service workers require a secure context:

navigator.serviceWorker.register('/sw.js');

This works only on:

https://

or

localhost

origins.


HTTPS Troubleshooting

ERR_CERT_AUTHORITY_INVALID

Cause:

Certificate is not trusted.

Fix:

  • Use mkcert
  • Import the certificate into the local trust store

ERR_CERT_COMMON_NAME_INVALID

Cause:

The hostname does not match the certificate.

Example:

Certificate issued for localhost
Accessing 127.0.0.1

Fix:

Generate the certificate with all required hostnames:

mkcert localhost 127.0.0.1

Port 443 Requires Elevated Privileges

Linux/macOS may require administrator privileges:

sudo http-server -S -p 443

Alternative:

Use a higher port:

https://localhost:8443

For most developers:

  1. Install mkcert
  2. Create a trusted local certificate
  3. Use http-server or your framework's HTTPS support
  4. Test using:
https://localhost

This provides the closest experience to a production HTTPS deployment while remaining simple to maintain.