> For the complete documentation index, see [llms.txt](https://docs.hivel.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.hivel.ai/self-managed-hivel-deployment/on-premises-generic/hivel-on-premises-deployment-guide-generic/https-setup-with-caddy-optional.md).

# HTTPS Setup with Caddy (Optional)

#### Why HTTPS Is Needed

The Hivel UI serves plain HTTP on port 3000. Modern browsers restrict APIs like `crypto.randomUUID` (used on sign-up/KYC pages) to secure contexts. Direct HTTP access fails with "crypto.randomUUID is not a function". Caddy is a lightweight Docker-based reverse proxy that terminates TLS in front of the UI.

### Prerequisites

Before proceeding, confirm:

* ✓ Hivel is running and healthy on port 3000
* ✓ Docker is installed on the host
* ✓ SSH access to the host
* ✓ (Option A only) A domain you control with DNS access

### Choose Your Option

**Option A: Domain + Let's Encrypt (Recommended)**\
Choose this if you have a domain available. Automatic certificate management, no manual renewal.

**Option B: Self-Signed Certificate (Testing Only)**\
Choose this only for testing before a domain is available. Not for production. Browsers show untrusted-certificate warnings.

## Option A: Domain + Let's Encrypt

### **Step 1: Configure DNS**

**Application/Location:** Your DNS provider

Create an A record pointing your subdomain to the host's public/reachable IP:

| **Record Type** | **Name**              | **Value**  |
| --------------- | --------------------- | ---------- |
| A               | hivel.yourcompany.com | \<host-ip> |

### **Step 2: Open Firewall Ports**

**Application/Location:** Your firewall/security group

Open:

* Port 80 (required for Let's Encrypt HTTP-01 challenge)
* Port 443 (HTTPS)

### **Step 3: Configure and Start Caddy**

**Application/Location:** Host (via SSH)

Run:

```
mkdir -p ~/caddy && cd ~/caddy
cat > Caddyfile <<'EOF'
hivel.yourcompany.com {
    reverse_proxy localhost:3000
}
EOF
docker run -d --name caddy --restart unless-stopped --network host \
  -v $(pwd)/Caddyfile:/etc/caddy/Caddyfile \
  -v caddy_data:/data \
  caddy:2
```

### **Step 4: Verify Certificate Issuance**

Run:

```
docker logs -f caddy
```

**Success:** Logs show "certificate obtained successfully"\
**Next:** Open `https://hivel.yourcompany.com` in a browser. UI loads with a valid certificate

## Option B: Self-Signed Certificate (Testing Only)

{% hint style="warning" %}
Use only for testing. Browsers show untrusted-certificate warnings. Never use in production.
{% endhint %}

### **Step 1: Open Firewall Port**

**Application/Location:** Your firewall/security group

Open port 443 only.

### **Step 2: Configure and Start Caddy**

**Application/Location:** Host (via SSH)

Run:

```
mkdir -p ~/caddy && cd ~/caddy
cat > Caddyfile <<'EOF'
:443 {
    tls internal
    reverse_proxy localhost:3000
}
EOF
docker run -d --name caddy --restart unless-stopped --network host \
  -v $(pwd)/Caddyfile:/etc/caddy/Caddyfile \
  -v caddy_data:/data \
  caddy:2

```

### **Step 3: Access the UI**

Open `https://<host-ip>` in a browser and click through the certificate warning.

#### Upgrade Option B to Option A (Add Domain Later)

When you obtain a domain, upgrade from self-signed to Let's Encrypt:

**Application/Location:** Host (via SSH)

Run:

```
cat > ~/caddy/Caddyfile <<'EOF'
hivel.yourcompany.com {
    reverse_proxy localhost:3000
}
EOF
docker restart caddy
```

**Result:** Caddy automatically requests a Let's Encrypt certificate on restart. Update firewall to allow ports 80 and 443.

## Managing Caddy

```
docker logs -f caddy
docker restart caddy
docker stop caddy
docker rm -f caddy   # app stays reachable on :3000 directly
```

**Certificates persist** in the `caddy_data` volume.

### Caddy Troubleshooting

Certificate not issued:

Run:

```
dig +short hivel.yourcompany.com
curl -I http://hivel.yourcompany.com
docker logs caddy --tail 100
```

Common causes: DNS not propagated, port 80 blocked, wrong IP.

***

**Connection Refused on Port 443**

**Application/Location:** Host (via SSH)

Run:

```
docker ps | grep caddy
```

Confirm firewall allows inbound 443.

**Blank Page or 502 Error**

**Application/Location:** Host (via SSH)

Run:

```
docker ps | grep insightly-ui
curl -I http://localhost:3000
```

If the UI container is unhealthy, see [Troubleshooting](/self-managed-hivel-deployment/on-premises-generic/hivel-on-premises-deployment-guide-generic/troubleshooting.md).

#### Configuration Reference:

<table data-header-hidden data-search="false"><thead><tr><th></th><th></th></tr></thead><tbody><tr><td><strong>Item</strong></td><td><strong>Value</strong></td></tr><tr><td>Reverse proxy</td><td>Caddy 2 (Docker container)</td></tr><tr><td>Ports opened</td><td>80 (Option A only), 443 (both options)</td></tr><tr><td>Certificate</td><td>Let's Encrypt (A) / self-signed (B)</td></tr><tr><td>Renewal</td><td>Automatic</td></tr><tr><td>Config</td><td>~/caddy/Caddyfile</td></tr><tr><td>Data volume</td><td>caddy_data</td></tr></tbody></table>

{% columns %}
{% column %} <a href="/self-managed-hivel-deployment/on-premises-generic/hivel-on-premises-deployment-guide-generic/verification-and-access.md" class="button primary" data-icon="backward">Back to Verification and Access</a>
{% endcolumn %}

{% column %}

{% endcolumn %}

{% column %} <a href="/self-managed-hivel-deployment/on-premises-generic/hivel-on-premises-deployment-guide-generic/file-locations-quick-reference.md" class="button primary" data-icon="forward">File Locations Quick Reference</a>
{% endcolumn %}
{% endcolumns %}
