---
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
