| name | local-ssl |
| description | Enable HTTPS for local development with trusted SSL certificates. Use when developers need to test SSL/TLS features, work with third-party APIs requiring HTTPS, or simulate production environments. Supports mkcert, OpenSSL, and automatic certificate trust configuration for macOS, Linux, and Windows. |
Local SSL/HTTPS
Enable HTTPS for local development environments with automatically trusted SSL certificates. This skill handles certificate generation, trust configuration, and web server setup.
Core Workflows
1. mkcert Setup (Recommended Approach)
mkcert is the simplest way to generate locally-trusted certificates.
set -e
RED='\033[0;31m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
NC='\033[0m'
echo -e "${BLUE}🔐 Setting up local SSL certificates...${NC}\n"
if ! command -v mkcert &> /dev/null; then
echo -e "${BLUE}📦 Installing mkcert...${NC}"
if [[ "$OSTYPE" == "darwin"* ]]; then
brew install mkcert nss
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
if command -v apt-get &> /dev/null; then
sudo apt-get install -y 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
fi
else
echo -e "${RED}❌ Unsupported OS. Please install mkcert manually.${NC}"
exit 1
fi
fi
mkdir -p certs
echo -e "${BLUE}🔒 Installing local Certificate Authority...${NC}"
mkcert -install
echo -e "${BLUE}🔑 Generating certificates...${NC}"
mkcert -cert-file certs/localhost.crt \
-key-file certs/localhost.key \
localhost 127.0.0.1 ::1 \
*.localhost \
local.dev \
*.local.dev
echo -e "\n${GREEN}✅ SSL certificates generated!${NC}"
echo -e "${BLUE}📍 Certificate files:${NC}"
echo -e " certs/localhost.crt"
echo -e " certs/localhost.key"
2. Docker Compose with SSL
version: '3.8'
services:
frontend:
build:
context: ./frontend
dockerfile: Dockerfile.dev
ports:
- "3000:3000"
- "3443:3443"
environment:
- HTTPS=true
- SSL_CRT_FILE=/certs/localhost.crt
- SSL_KEY_FILE=/certs/localhost.key
volumes:
- ./frontend:/app
- /app/node_modules
- ./certs:/certs:ro
networks:
- app-network
api:
build:
context: ./backend
dockerfile: Dockerfile.dev
ports:
- "8000:8000"
- "8443:8443"
environment:
- SSL_CERT=/certs/localhost.crt
- SSL_KEY=/certs/localhost.key
volumes:
3. Nginx SSL Configuration
# Generate: nginx/nginx-ssl.conf
# Frontend HTTPS
server {
listen 443 ssl http2;
server_name localhost local.dev;
ssl_certificate /etc/nginx/certs/localhost.crt;
ssl_certificate_key /etc/nginx/certs/localhost.key;
# SSL Configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
location / {
proxy_pass http://frontend:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# API HTTPS
server {
listen 443 ssl http2;
server_name api.localhost api.local.dev;
ssl_certificate /etc/nginx/certs/localhost.crt;
ssl_certificate_key /etc/nginx/certs/localhost.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://api:8000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# HTTP to HTTPS redirect
server {
listen 80;
server_name localhost local.dev api.localhost api.local.dev;
return 301 https://$host$request_uri;
}
4. Vite HTTPS Configuration
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import fs from 'fs';
import path from 'path';
export default defineConfig({
plugins: [react()],
server: {
host: '0.0.0.0',
port: 3000,
https: process.env.HTTPS === 'true' ? {
key: fs.readFileSync(path.resolve(__dirname, '../certs/localhost.key')),
cert: fs.readFileSync(path.resolve(__dirname, '../certs/localhost.crt')),
} : undefined,
watch: {
usePolling: true,
},
},
});
5. Node.js/Express HTTPS Setup
import express from 'express';
import https from 'https';
import http from 'http';
import fs from 'fs';
import path from 'path';
const app = express();
app.use(express.json());
app.get('/health', (req, res) => {
res.json({ status: 'ok', protocol: req.protocol });
});
const PORT = process.env.PORT || 8000;
const HTTPS_PORT = 8443;
http.createServer(app).listen(PORT, () => {
console.log(`HTTP server running on http://localhost:${PORT}`);
});
if (process.env. && process..) {
{
httpsOptions = {
: fs.(process..),
: fs.(process..),
};
https.(httpsOptions, app).(, {
.();
});
} (error) {
.(, error);
}
}
6. OpenSSL Alternative (Manual)
For systems where mkcert isn't available:
set -e
mkdir -p certs
openssl genrsa -out certs/localhost.key 2048
openssl req -new -key certs/localhost.key -out certs/localhost.csr \
-subj "/C=US/ST=State/L=City/O=Development/CN=localhost"
openssl x509 -req -days 365 -in certs/localhost.csr \
-signkey certs/localhost.key -out certs/localhost.crt \
-extfile <(printf "subjectAltName=DNS:localhost,DNS:*.localhost,IP:127.0.0.1")
rm certs/localhost.csr
echo "✅ Certificates generated!"
echo "⚠️ You'll need to manually trust the certificate in your browser"
7. Certificate Trust Scripts
RED='\033[0;31m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
NC='\033[0m'
CERT_FILE="certs/localhost.crt"
if [ ! -f "$CERT_FILE" ]; then
echo -e "${RED}❌ Certificate not found: $CERT_FILE${NC}"
exit 1
fi
echo -e "${BLUE}🔒 Adding certificate to system trust store...${NC}"
if [[ "$OSTYPE" == "darwin"* ]]; then
sudo security add-trusted-cert -d -r trustRoot \
-k /Library/Keychains/System.keychain "$CERT_FILE"
echo -e "${GREEN}✅ Certificate trusted on macOS${NC}"
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
sudo cp "$CERT_FILE" /usr/local/share/ca-certificates/localhost.crt
sudo update-ca-certificates
echo -e "${GREEN}✅ Certificate trusted on Linux${NC}"
else
echo -e
Environment Configuration
HTTPS=true
SSL_CERT_FILE=./certs/localhost.crt
SSL_KEY_FILE=./certs/localhost.key
VITE_API_URL=https://localhost:8443
API_BASE_URL=https://localhost:8443
Testing SSL Setup
echo "🧪 Testing SSL setup..."
if curl -k -s https://localhost:3443 > /dev/null 2>&1; then
echo "✅ Frontend HTTPS is working"
else
echo "❌ Frontend HTTPS failed"
fi
if curl -k -s https://localhost:8443/health > /dev/null 2>&1; then
echo "✅ API HTTPS is working"
else
echo "❌ API HTTPS failed"
fi
echo ""
echo "📋 Certificate details:"
openssl x509 -in certs/localhost.crt -noout -subject -dates
Makefile Integration
.PHONY: ssl-setup ssl-test ssl-clean
ssl-setup: ## Generate and trust SSL certificates
@./scripts/setup-ssl.sh
ssl-test: ## Test SSL configuration
@./scripts/test-ssl.sh
ssl-clean: ## Remove SSL certificates
@rm -rf certs
@echo "✅ SSL certificates removed"
Best Practices
- Never commit certificates - Add
certs/ to .gitignore
- Use mkcert for development - Easiest and most reliable
- Automate setup - Include in onboarding scripts
- Test in all browsers - Chrome, Firefox, Safari, Edge
- Document the process - Help teammates get started quickly
- Use consistent domains - *.localhost or *.local.dev
Integration with zero-to-running
When both skills are active:
- SSL is set up automatically during
make dev
- Services available over HTTPS
- Certificates automatically trusted
- Environment variables configured correctly
Troubleshooting
Certificate not trusted:
mkcert -uninstall
mkcert -install
Port conflicts:
HTTPS_PORT=4443
Browser shows NET::ERR_CERT_AUTHORITY_INVALID:
- Run
mkcert -install again
- Restart browser completely
- Check system keychain for certificate