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.
Install via CLI
openskills install majiayu000/claude-skill-registry---
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.
```bash
# Generate: scripts/setup-ssl.sh
#!/bin/bash
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"
# Check if mkcert is installed
if ! command -v mkcert &> /dev/null; then
echo -e "${BLUE}📦 Installing mkcert...${NC}"
# Detect OS and install mkcert
if [[ "$OSTYPE" == "darwin"* ]]; then
# macOS
brew install mkcert nss # nss for Firefox support
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
# Linux
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
# Create certs directory
mkdir -p certs
# Install local CA
echo -e "${BLUE}🔒 Installing local Certificate Authority...${NC}"
mkcert -install
# Generate certificates for localhost and custom domains
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
```yaml
# Update docker-compose.yml to mount certificates
version: '3.8'
services:
frontend:
build:
context: ./frontend
dockerfile: Dockerfile.dev
ports:
- "3000:3000"
- "3443:3443" # HTTPS port
environment:
- HTTPS=true
- SSL_CRT_FILE=/certs/localhost.crt
- SSL_KEY_FILE=/certs/localhost.key
volumes:
- ./frontend:/app
- /app/node_modules
- ./certs:/certs:ro # Mount certificates as read-only
networks:
- app-network
api:
build:
context: ./backend
dockerfile: Dockerfile.dev
ports:
- "8000:8000"
- "8443:8443" # HTTPS port
environment:
- SSL_CERT=/certs/localhost.crt
- SSL_KEY=/certs/localhost.key
volumes:
- ./backend:/app
- /app/node_modules
- ./certs:/certs:ro
networks:
- app-network
nginx:
image: nginx:alpine
ports:
- "443:443"
volumes:
- ./nginx/nginx-ssl.conf:/etc/nginx/conf.d/default.conf:ro
- ./certs:/etc/nginx/certs:ro
depends_on:
- frontend
- api
networks:
- app-network
```
### 3. Nginx SSL Configuration
```nginx
# 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
```typescript
// Generate: frontend/vite.config.ts (with HTTPS)
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
```typescript
// Generate: backend/src/server.ts (with HTTPS)
import express from 'express';
import https from 'https';
import http from 'http';
import fs from 'fs';
import path from 'path';
const app = express();
// Your middleware and routes here
app.use(express.json());
// Health check
app.get('/health', (req, res) => {
res.json({ status: 'ok', protocol: req.protocol });
});
const PORT = process.env.PORT || 8000;
const HTTPS_PORT = 8443;
// Start HTTP server
http.createServer(app).listen(PORT, () => {
console.log(`HTTP server running on http://localhost:${PORT}`);
});
// Start HTTPS server if certificates exist
if (process.env.SSL_CERT && process.env.SSL_KEY) {
try {
const httpsOptions = {
key: fs.readFileSync(process.env.SSL_KEY),
cert: fs.readFileSync(process.env.SSL_CERT),
};
https.createServer(httpsOptions, app).listen(HTTPS_PORT, () => {
console.log(`HTTPS server running on https://localhost:${HTTPS_PORT}`);
});
} catch (error) {
console.error('Failed to start HTTPS server:', error);
}
}
```
### 6. OpenSSL Alternative (Manual)
For systems where mkcert isn't available:
```bash
# Generate: scripts/generate-certs-openssl.sh
#!/bin/bash
set -e
mkdir -p certs
# Generate private key
openssl genrsa -out certs/localhost.key 2048
# Generate certificate signing request
openssl req -new -key certs/localhost.key -out certs/localhost.csr \
-subj "/C=US/ST=State/L=City/O=Development/CN=localhost"
# Generate self-signed certificate
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")
# Clean up CSR
rm certs/localhost.csr
echo "✅ Certificates generated!"
echo "⚠️ You'll need to manually trust the certificate in your browser"
```
### 7. Certificate Trust Scripts
```bash
# Generate: scripts/trust-cert.sh
#!/bin/bash
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
# macOS
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
# Linux
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 "${RED}❌ Unsupported OS for automatic trust${NC}"
echo "Please manually trust the certificate:"
echo " - Chrome: chrome://settings/certificates"
echo " - Firefox: about:preferences#privacy"
fi
```
## Environment Configuration
```bash
# Add to .env
# SSL Configuration
HTTPS=true
SSL_CERT_FILE=./certs/localhost.crt
SSL_KEY_FILE=./certs/localhost.key
# Update URLs to use HTTPS
VITE_API_URL=https://localhost:8443
API_BASE_URL=https://localhost:8443
```
## Testing SSL Setup
```bash
# Generate: scripts/test-ssl.sh
#!/bin/bash
echo "🧪 Testing SSL setup..."
# Test HTTPS endpoint
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
# Check certificate validity
echo ""
echo "📋 Certificate details:"
openssl x509 -in certs/localhost.crt -noout -subject -dates
```
## Makefile Integration
```makefile
.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
1. **Never commit certificates** - Add `certs/` to .gitignore
2. **Use mkcert for development** - Easiest and most reliable
3. **Automate setup** - Include in onboarding scripts
4. **Test in all browsers** - Chrome, Firefox, Safari, Edge
5. **Document the process** - Help teammates get started quickly
6. **Use consistent domains** - *.localhost or *.local.dev
## Integration with zero-to-running
When both skills are active:
1. SSL is set up automatically during `make dev`
2. Services available over HTTPS
3. Certificates automatically trusted
4. Environment variables configured correctly
## Troubleshooting
**Certificate not trusted:**
```bash
# Reinstall CA
mkcert -uninstall
mkcert -install
```
**Port conflicts:**
```bash
# Change ports in .env
HTTPS_PORT=4443
```
**Browser shows NET::ERR_CERT_AUTHORITY_INVALID:**
- Run `mkcert -install` again
- Restart browser completely
- Check system keychain for certificate
Scanned 2/12/2026
No comments yet. Be the first to comment!