SSL / HTTPS Configuration
ZIRI supports optional HTTPS for both the proxy server and the frontend development server. SSL is opt-in and backwards compatible: if you do not configure certificates, everything runs over HTTP as before. You can use any PEM-format certificate, including mkcert, Let’s Encrypt, and corporate CA–issued certificates.
Overview
When you enable SSL:
- The proxy server listens on HTTPS instead of HTTP.
- The Nuxt frontend dev server uses HTTPS in development when it finds local certificates.
- Server-to-server calls from the dev server to the proxy trust your local CA without disabling TLS verification.
If SSL is disabled or misconfigured, ZIRI falls back to plain HTTP.
How SSL Is Implemented
Proxy server
File: packages/proxy/src/server.ts
- When
ssl.enabledistruein the configuration, the proxy useshttps.createServer()with the configured certificate and key. - If SSL is disabled or the certificate files are missing, the proxy starts as a plain HTTP server.
- You configure SSL using
config.jsonor environment variables (see Configuration Reference).
Nuxt frontend dev server
File: packages/ui/nuxt.config.ts
- The dev server auto-detects certificates at
../../certs/cert.pemand../../certs/key.pemrelative topackages/ui/. - When these files exist,
devServer.httpsis enabled automatically in development. - The default
proxyUrlswitches fromhttp://tohttps://when HTTPS is enabled so the UI talks to the HTTPS proxy. - The private key is only read by the dev server at runtime in development and is not used during build.
Server-to-server trust in dev
File: packages/ui/package.json
- The
devscript setsNODE_EXTRA_CA_CERTS=../../certs/rootCA.pemviacross-env. - Node.js adds this CA to its existing trust store. Nitro’s server-side
fetchcalls can then talk to the HTTPS proxy without errors. - This is the standard Node.js approach and does not disable TLS verification.
- This applies only to
npm run dev, not to production or build steps.
Prerequisites
For local development, the simplest setup uses mkcert to create a local CA and certificates that your OS and browser trust.
Install mkcert:
Windows (PowerShell):
choco install mkcert # or: scoop install mkcertmacOS (Homebrew):
brew install mkcertLinux:
sudo apt install libnss3-tools brew install mkcert
You run mkcert -install once per machine to install the local CA into your trust stores.
Generating Certificates (Local Development)
Run these commands from the project root.
Bash (macOS / Linux / WSL)
mkcert -install
mkdir -p certs
mkcert -key-file ./certs/key.pem -cert-file ./certs/cert.pem localhost 127.0.0.1
cp "$(mkcert -CAROOT)/rootCA.pem" ./certs/rootCA.pemWindows (PowerShell)
mkcert -install
mkdir certs
mkcert -key-file .\certs\key.pem -cert-file .\certs\cert.pem localhost 127.0.0.1
Copy-Item "$(mkcert -CAROOT)\rootCA.pem" .\certs\rootCA.pemYou now have:
certs/cert.pem— certificate forlocalhostand127.0.0.1certs/key.pem— private keycerts/rootCA.pem— mkcert root CA (for dev server-to-proxy trust)
The certs/ directory is gitignored by default.
Configuring the Proxy Server
The proxy reads configuration from config.json and environment variables.
Config file
On each OS, the config file is stored here:
- Windows:
%APPDATA%\ziri\config.json - macOS / Linux:
~/.ziri/config.json
Add an ssl section:
{
"ssl": {
"enabled": true,
"cert": "/absolute/path/to/certs/cert.pem",
"key": "/absolute/path/to/certs/key.pem"
}
}Use absolute paths for cert and key. The proxy logs when SSL is enabled and may log a message such as [SERVER] SSL enabled but cert/key files not found if the paths are incorrect.
Environment variables
You can override the config file with environment variables:
SSL_ENABLED=trueSSL_CERT_PATH=/absolute/path/to/certs/cert.pemSSL_KEY_PATH=/absolute/path/to/certs/key.pem
These take precedence over the ssl section in config.json.
Frontend Dev Server
When you run the dev environment from the repo root:
npm run devthe frontend:
- Looks for
certs/cert.pemandcerts/key.pemat the project root. - Enables HTTPS for the Nuxt dev server when both files exist.
- Reads
certs/rootCA.pemthroughNODE_EXTRA_CA_CERTSso Nitro’s server-side fetch calls trust the local proxy certificate. - Auto-switches the default
proxyUrltohttps://localhost:3100when HTTPS is available.
You do not need to set NODE_EXTRA_CA_CERTS manually. It is already part of the dev script in packages/ui/package.json.
If you want to override the proxy URL from the frontend, you can set:
NUXT_PUBLIC_PROXY_URL=https://localhost:3100
This should match the scheme and host you use for the proxy.
Production and Docker
For Docker-based deployments, you can mount your certificates into the container and point the proxy at them.
Example docker-compose.yml:
services:
proxy:
image: ziri/proxy:latest
ports:
- "3100:3100"
volumes:
- ziri-data:/data
- ./certs:/certs:ro
environment:
- CONFIG_DIR=/data
- HOST=0.0.0.0
- SSL_ENABLED=true
- SSL_CERT_PATH=/certs/cert.pem
- SSL_KEY_PATH=/certs/key.pem
restart: unless-stopped
volumes:
ziri-data:For public-facing deployments, you usually terminate TLS at a reverse proxy (nginx, Caddy, Traefik, etc.) with Let’s Encrypt and run ZIRI behind it over HTTP. This keeps certificate management in one place and lets you reuse existing infrastructure.
Using Custom Certificates
ZIRI works with any valid PEM certificate and private key pair:
- Let’s Encrypt: you can use
fullchain.pemascertandprivkey.pemaskey. - Corporate CA: use the certificate and key issued by your internal PKI. Import the CA into your OS/browser trust store as needed.
- Self-signed certificates: you can point ZIRI at any self-signed certificate, but browsers and Node.js will show warnings unless you trust the issuing CA (mkcert solves this for local development).
For development, mkcert is strongly recommended because it installs a root CA that browsers and Node trust.
Configuration Reference
Config file (summary)
{
"ssl": {
"enabled": true,
"cert": "/absolute/path/to/cert.pem",
"key": "/absolute/path/to/cert.pem"
}
}Environment variables
SSL_ENABLED=trueSSL_CERT_PATH=/path/to/cert.pemSSL_KEY_PATH=/path/to/key.pem
Frontend environment variable
NUXT_PUBLIC_PROXY_URL=https://localhost:3100
Certificate storage
From the project root:
certs/cert.pemcerts/key.pemcerts/rootCA.pem
Troubleshooting
“fetch failed” or “503 Proxy server unavailable” in dev
The most common cause is a missingcerts/rootCA.pem. Re-run thecp/Copy-Itemcommand from the certificate generation step sorootCA.pemexists where the dev script expects it.Browser shows “Not Secure” or certificate warning
Runmkcert -installto add the mkcert CA to your trust stores, then restart your browser. Make sure you are using the certificate generated by mkcert, not a different self-signed certificate.Proxy falls back to HTTP
Check thatSSL_ENABLEDis set or"ssl.enabled": trueis present inconfig.json. Verify thatcertandkeypaths are absolute and point to existing files. Look for messages like[SERVER] SSL enabled but cert/key files not foundin the proxy logs.TLS errors in dev but proxy works in the browser
This usually means Node.js does not trust your CA. Confirm thatNODE_EXTRA_CA_CERTS=../../certs/rootCA.pemis present in thedevscript inpackages/ui/package.jsonand thatcerts/rootCA.pemexists.
Security Notes
- ZIRI does not use
NODE_TLS_REJECT_UNAUTHORIZED=0anywhere. NODE_EXTRA_CA_CERTSextends the trust store instead of disabling certificate verification.- Certificates and private keys live in
certs/, which is gitignored by default. - Private keys are not bundled into builds; they are only read at runtime by the proxy and the dev server when you enable HTTPS.
Disabling SSL
To run everything over HTTP again:
- Remove the
sslsection fromconfig.jsonor set"enabled": false. - Optionally unset
SSL_ENABLEDand related environment variables. - You can also delete or move the
certs/directory if you no longer need local certificates.
When SSL is disabled or misconfigured, both the proxy and the frontend dev server revert to HTTP automatically.