zerobyte/apps/docs/content/docs/guides/reverse-proxy.mdx
ivenos 38b49debb1
Rename docker-compose.yml to compose.yaml
Aligns the project with the current Compose Specification, which
designates compose.yaml as the canonical filename and treats the
docker-compose.yml name as a legacy fallback.

Renames every compose file in the repo (the root dev/e2e stack, the
deployment examples under examples/, and the integration-test infra
stack) and updates all documentation, the integration test runner, the
capability hint messages, and the .gitattributes pattern accordingly.
No top-level version field was present to remove.

Functional behavior is unchanged: docker compose discovers either
filename, so existing deployments are not affected by the rename.

Reference: https://docs.docker.com/compose/intro/compose-application-model/
2026-06-02 21:09:47 +02:00

193 lines
5.6 KiB
Text

---
title: Reverse Proxy
description: Run Zerobyte behind Nginx, Caddy, or Traefik with HTTPS
---
When running Zerobyte behind a reverse proxy, you need to configure the `BASE_URL` environment variable with your HTTPS domain and ensure your proxy passes the correct headers.
## Prerequisites
Set the following environment variables in your `compose.yaml`:
```yaml compose.yaml
environment:
- BASE_URL=https://zerobyte.example.com
- TRUST_PROXY=true # Optional: trust X-Forwarded-For headers from your proxy
```
Restart the container after making changes:
```bash
docker compose down && docker compose up -d
```
## How BASE_URL affects cookie security
Zerobyte uses `BASE_URL` to determine whether authentication cookies should be marked as `Secure`. This directly affects how login sessions work:
| BASE_URL value | Cookie behavior |
|---|---|
| `http://` or IP address (e.g., `http://192.168.1.100:4096`) | Secure cookies **disabled**, cookies sent over HTTP. Suitable for local network access. |
| `https://` with domain (e.g., `https://zerobyte.example.com`) | Secure cookies **enabled**, cookies only sent over HTTPS. Required for production. |
<Callout type="warn">
If `BASE_URL` is set to an `https://` address, browsers will **only** send authentication cookies over HTTPS connections. Accessing Zerobyte over plain HTTP will fail with login loops or session errors. Make sure your reverse proxy terminates TLS before forwarding to Zerobyte.
</Callout>
## Proxy configurations
<Tabs items={["Nginx", "Caddy", "Traefik"]}>
<Tab value="Nginx">
### Nginx
Create a server block for Zerobyte. This configuration handles TLS termination and forwards requests to the container:
```nginx nginx.conf
server {
listen 443 ssl http2;
server_name zerobyte.example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://localhost:4096;
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;
}
}
```
Reload Nginx after saving:
```bash
sudo nginx -t && sudo systemctl reload nginx
```
</Tab>
<Tab value="Caddy">
### Caddy
Caddy automatically provisions and renews TLS certificates from Let's Encrypt. Add this to your `Caddyfile`:
```text Caddyfile
zerobyte.example.com {
reverse_proxy localhost:4096
}
```
Reload Caddy after saving:
```bash
sudo systemctl reload caddy
```
Caddy automatically handles `X-Forwarded-For`, `X-Forwarded-Proto`, and TLS, with no extra configuration needed.
</Tab>
<Tab value="Traefik">
### Traefik
If you run Traefik as your reverse proxy, add labels to the Zerobyte service in your `compose.yaml`:
```yaml compose.yaml
services:
zerobyte:
image: ghcr.io/nicotsx/zerobyte:v0.37
container_name: zerobyte
restart: unless-stopped
environment:
- BASE_URL=https://zerobyte.example.com
- APP_SECRET=your-secret-here
labels:
- "traefik.enable=true"
- "traefik.http.routers.zerobyte.rule=Host(`zerobyte.example.com`)"
- "traefik.http.routers.zerobyte.entrypoints=websecure"
- "traefik.http.routers.zerobyte.tls.certresolver=letsencrypt"
- "traefik.http.services.zerobyte.loadbalancer.server.port=4096"
volumes:
- /etc/localtime:/etc/localtime:ro
- /var/lib/zerobyte:/var/lib/zerobyte
```
Make sure your Traefik instance is configured with a `websecure` entrypoint and a `letsencrypt` certificate resolver.
</Tab>
</Tabs>
## Binding to localhost
If you are using a reverse proxy on the same machine, bind the Zerobyte port to `127.0.0.1` so it is not directly accessible from the network:
```yaml compose.yaml
ports:
- "127.0.0.1:4096:4096"
```
This ensures all traffic goes through your reverse proxy, which handles TLS and authentication headers.
## Security considerations
<Callout type="warn">
**Exposing Zerobyte to the internet requires care.** Even behind a reverse proxy with HTTPS, make sure you:
- Bind the container port to localhost only (`127.0.0.1:4096:4096`) so it cannot be accessed directly.
- Use a strong `APP_SECRET` (generated with `openssl rand -hex 32`).
- Keep Zerobyte updated to the latest version.
- Consider placing Zerobyte behind a secure tunnel (Cloudflare Tunnel, Tailscale, WireGuard) for an additional layer of protection.
</Callout>
### A note on TRUSTED_ORIGINS
The `TRUSTED_ORIGINS` environment variable allows you to add additional CORS origins for cross-origin requests. It does **not** make HTTP work when `BASE_URL` is set to HTTPS. If your `BASE_URL` uses `https://`, all access must go through HTTPS regardless of what is listed in `TRUSTED_ORIGINS`.
```yaml
# TRUSTED_ORIGINS only adds allowed CORS origins, it does NOT downgrade HTTPS to HTTP
environment:
- BASE_URL=https://zerobyte.example.com
- TRUSTED_ORIGINS=https://other-app.example.com
```
## Verifying your setup
After configuring your reverse proxy:
<Steps>
<Step>
### Test HTTPS access
Open `https://zerobyte.example.com` in your browser. You should see the Zerobyte login page with a valid TLS certificate.
</Step>
<Step>
### Check headers
Verify that your proxy is forwarding the correct headers. In the Zerobyte container logs, you should see requests coming from your proxy:
```bash
docker compose logs -f zerobyte
```
</Step>
<Step>
### Confirm login works
Log in with your admin credentials. If login fails or loops, double-check that `BASE_URL` matches the URL you are accessing and that cookies are being set correctly over HTTPS.
</Step>
</Steps>
import { Step, Steps } from "fumadocs-ui/components/steps";
import { Tab, Tabs } from "fumadocs-ui/components/tabs";