Skip to content

Local HTTPS Development Setup

Get trusted HTTPS certificates for local development using mkcert + Caddy.

  • Test HTTPS-only features (Service Workers, HTTP/3, etc.)
  • Match production environment
  • No browser security warnings
  • Test with real certificates
chat.local {
tls internal # Self-signed cert
reverse_proxy faster-chat:8787
encode gzip zstd
}
Terminal window
echo "127.0.0.1 chat.local" | sudo tee -a /etc/hosts
Terminal window
docker compose -f docker-compose.yml -f docker-compose.caddy.yml restart caddy

https://chat.local

Note: Browser will show security warning - click “Advanced” → “Proceed”


Section titled “Method 2: mkcert (Trusted Certificates - Recommended!)”

Arch Linux:

Terminal window
sudo pacman -S mkcert nss

Debian/Ubuntu:

Terminal window
sudo apt install libnss3-tools
curl -JLO "https://dl.filippo.io/mkcert/latest?for=linux/amd64"
chmod +x mkcert-v*-linux-amd64
sudo mv mkcert-v*-linux-amd64 /usr/local/bin/mkcert

macOS:

Terminal window
brew install mkcert
Terminal window
mkcert -install

This installs a local CA in your system trust store - all browsers will trust it!

Terminal window
cd /home/mikekey/Projects/1_React/faster-chat
# Create certs directory
mkdir -p certs
# Generate cert for chat.local
mkcert -cert-file certs/cert.pem -key-file certs/key.pem \
chat.local localhost 127.0.0.1 ::1
chat.local {
tls /etc/caddy/certs/cert.pem /etc/caddy/certs/key.pem
reverse_proxy faster-chat:8787
encode gzip zstd
# Optional: Security headers
header {
Strict-Transport-Security "max-age=31536000"
X-Content-Type-Options "nosniff"
}
}
# Also work with localhost HTTPS
localhost {
tls /etc/caddy/certs/cert.pem /etc/caddy/certs/key.pem
reverse_proxy faster-chat:8787
}

Update docker-compose.caddy.yml:

services:
caddy:
image: caddy:2-alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- ./certs:/etc/caddy/certs:ro # Add this line
- caddy-data:/data
- caddy-config:/config
depends_on:
- faster-chat
Terminal window
echo "127.0.0.1 chat.local" | sudo tee -a /etc/hosts
Terminal window
docker compose -f docker-compose.yml -f docker-compose.caddy.yml down
docker compose -f docker-compose.yml -f docker-compose.caddy.yml up -d

Want to access from phone/tablet on your local network?

Terminal window
ip addr show | grep "inet 192.168"
# Example: 192.168.1.100
Terminal window
mkcert -cert-file certs/cert.pem -key-file certs/key.pem \
chat.local localhost 127.0.0.1 ::1 \
192.168.1.100 # Your actual LAN IP
chat.local, localhost, 192.168.1.100 {
tls /etc/caddy/certs/cert.pem /etc/caddy/certs/key.pem
reverse_proxy faster-chat:8787
}

On your phone/tablet, install the mkcert CA certificate:

Terminal window
# On your computer, get the CA cert
cat "$(mkcert -CAROOT)/rootCA.pem"
# Copy output, paste into a file on your phone
# Install it in Settings → Security → Install Certificate

Then access: https://192.168.1.100


Method 4: Tailscale + Let’s Encrypt (Real Certs!)

Section titled “Method 4: Tailscale + Let’s Encrypt (Real Certs!)”

If you use Tailscale, you can get real Let’s Encrypt certificates for your tailnet domain!

Terminal window
# Enable MagicDNS and HTTPS certificates in Tailscale admin
your-machine-name.your-tailnet.ts.net {
# Caddy automatically gets Let's Encrypt cert!
reverse_proxy faster-chat:8787
}

https://your-machine-name.your-tailnet.ts.net

Real certificate, works everywhere on your Tailscale network!


If mkcert-generated certs aren’t trusted:

Terminal window
# Reinstall CA
mkcert -uninstall
mkcert -install
# Restart browser completely (not just refresh)

Check file permissions:

Terminal window
ls -la certs/
# Should be readable by all
chmod 644 certs/*.pem

Check Docker mount:

Terminal window
docker exec faster-chat-caddy-1 ls -la /etc/caddy/certs/

Chrome caches HSTS. Clear it:

chrome://net-internals/#hsts
→ Delete domain security policies for "chat.local"

Firefox uses its own certificate store:

Terminal window
# Find your Firefox profile
ls ~/.mozilla/firefox/
# mkcert should auto-install, but verify:
certutil -d sql:~/.mozilla/firefox/PROFILE.default -L

MethodTrust LevelSetup TimeUse Case
Self-signed❌ Untrusted30 secQuick testing
mkcert✅ Trusted2 minLocal development
Tailscale✅ Real CA5 minRemote access
LAN IP + mkcert✅ Trusted*5 minTesting on devices

*Requires installing CA on each device


Terminal window
# 1. Install mkcert
sudo pacman -S mkcert nss # Arch
# or: brew install mkcert # macOS
# 2. Create local CA
mkcert -install
# 3. Generate certificates
cd /path/to/faster-chat
mkdir -p certs
mkcert -cert-file certs/cert.pem -key-file certs/key.pem \
chat.local localhost 127.0.0.1 ::1
# 4. Add to hosts
echo "127.0.0.1 chat.local" | sudo tee -a /etc/hosts
# 5. Update docker-compose.caddy.yml to mount certs/
# 6. Update Caddyfile with TLS paths
# 7. Restart
docker compose -f docker-compose.yml -f docker-compose.caddy.yml up -d
# 8. Access
open https://chat.local

You’ll have fully trusted HTTPS for local development! 🔒✨