TLS / HTTPS
envGo can serve HTTPS itself with an auto-generated certificate, or run plain HTTP behind a reverse proxy that terminates TLS. For anything public, prefer the reverse proxy.
Development: --tls
envgo --tlsAt startup envGo generates a self-signed certificate and serves HTTPS:
[envGo] envGo HTTPS running -> https://127.0.0.1:8080/Certificate properties
| Property | Value |
|---|---|
| Key type | RSA 2048-bit |
| Validity | 1 year from start |
| Subject alternative names | localhost, 127.0.0.1 |
| Storage | Generated fresh in memory at each start, never written to disk |
| Minimum protocol | TLS 1.2 |
Because the certificate is regenerated on every start, its fingerprint changes each time. Anything that pinned the old certificate will need updating.
Trusting the warning once
Your browser will warn about the self-signed certificate:
- Click Advanced
- Click Proceed to 127.0.0.1 (unsafe)
This is expected in development.
Full command
envgo --tls --dir . --env .env --allow api.openai.comWhy not --tls in production
- Self-signed certificates produce browser warnings for every visitor
- There is no automatic renewal — a fresh certificate is minted per process start
- You would have to distribute and trust your certificate out of band
Instead, terminate TLS at a reverse proxy and let envGo speak plain HTTP on
127.0.0.1.
Production option 1 — Caddy
# Debian / Ubuntusudo apt install -y caddyyourdomain.com { reverse_proxy 127.0.0.1:8080}sudo systemctl enable caddysudo systemctl start caddyCaddy obtains a Let’s Encrypt certificate on first request, renews it automatically, and redirects HTTP to HTTPS.
Which headers envGo sets itself
envGo adds X-Frame-Options: DENY, a restrictive Content-Security-Policy, and
Cache-Control: no-store on the dashboard routes only — /__envgo_dashboard
and its data endpoint. Your own pages and API responses are forwarded as they
are, so any site-wide headers are your responsibility (or Caddy’s).
Production option 2 — nginx
sudo apt install -y nginxsudo nano /etc/nginx/sites-available/envgoserver { listen 443 ssl; server_name yourdomain.com;
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3;
location / { proxy_pass http://127.0.0.1:8080; 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; }}
server { listen 80; server_name yourdomain.com; return 301 https://$host$request_uri;}sudo ln -s /etc/nginx/sites-available/envgo /etc/nginx/sites-enabled/sudo nginx -tsudo systemctl restart nginxProduction option 3 — certbot standalone
If you would rather manage certificates yourself:
sudo apt install -y certbotsudo certbot certonly --standalone -d yourdomain.com/etc/letsencrypt/live/yourdomain.com/fullchain.pem/etc/letsencrypt/live/yourdomain.com/privkey.pemPoint your proxy config at those paths. Note that --standalone briefly needs
port 80 free, so stop whatever holds it during issuance.
Setting trust_proxy
When a reverse proxy sits in front of envGo, enable it in the routes config so rate limiting can tell clients apart:
{ "trust_proxy": true, "default_rate_limit": "30/min", "scrub_response": true, "routes": []}trust_proxy: true makes envGo read the client IP from the first entry of
X-Forwarded-For instead of the TCP peer address. Without it, every request
behind a proxy looks like it comes from the proxy, so all clients share one
rate-limit bucket.
Security headers, if you want them
For nginx:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;add_header X-Content-Type-Options "nosniff" always;add_header X-Frame-Options "DENY" always;add_header Referrer-Policy "strict-origin-when-cross-origin" always;X-XSS-Protection is obsolete in modern browsers and is omitted deliberately —
a restrictive Content-Security-Policy is the effective control instead.
Firewall
# UFW (Debian / Ubuntu)sudo ufw allow 22/tcpsudo ufw allow 80/tcpsudo ufw allow 443/tcpsudo ufw enable
# firewalld (Fedora / RHEL)sudo firewall-cmd --permanent --add-service=sshsudo firewall-cmd --permanent --add-service=httpsudo firewall-cmd --permanent --add-service=httpssudo firewall-cmd --reloadPort 8080 must stay closed — envGo binds 127.0.0.1 and is reachable only through
the proxy.
Common issues
Certificate expired
With Caddy this should not happen — renewal is automatic. For manual setups:
sudo certbot renewsudo systemctl restart nginxSSL handshake failed
Check the certificate paths and that the proxy process can read them:
sudo ls -la /etc/letsencrypt/live/yourdomain.com/Mixed content warnings
Every asset must load over HTTPS:
<!-- Bad --><script src="http://example.com/script.js"></script>
<!-- Good --><script src="https://example.com/script.js"></script>API calls fail only over HTTPS
If your frontend is served over HTTPS, it must call envGo over HTTPS too — a
browser will block http://127.0.0.1 requests from an HTTPS page. Route them
through the same origin (for example /api/chat) via the reverse proxy.
Best practices
- Use
--tlsfor local development only - Terminate TLS at Caddy or nginx in production
- Set
trust_proxy: trueonly behind a proxy you control - Add security headers explicitly — nothing adds them for you
- Forward requests through the same origin so the browser never mixes schemes
- Keep certificate paths readable by the proxy service user
Next steps
- Deploy to VPS — full production walkthrough
- Security Model — the TLS layer in context
- Configuration —
trust_proxyand friends