Reverse Proxy Setup
Crit Alarm should always run behind a reverse proxy that terminates TLS (HTTPS).
Because Crit Alarm relies on authentication headers and custom ntfy headers (X-Priority, X-Title, X-Tags, etc.), your reverse proxy must pass all headers through intact.
What breaks if headers are stripped
Section titled “What breaks if headers are stripped”- Authentication: If a proxy drops or strips the
Authorizationheader, every webhook publish and management call fails with401 Unauthorized. - Priority Escalation: If
X-Priorityis stripped, alerts default to priority 3 (low/info). They will not open an incident or sound an emergency siren on your phone. - Correct client IP: with
X-Forwarded-Forpreserved, rate limiting counts the client, not your proxy.
1. Caddy Configuration
Section titled “1. Caddy Configuration”Caddy automatically handles Let’s Encrypt TLS certificates and passes HTTP headers through by default.
Caddyfile Example
Section titled “Caddyfile Example”alerts.example.com { reverse_proxy 127.0.0.1:8080 { header_up Host {host} header_up X-Real-IP {remote_host} header_up X-Forwarded-For {remote_host} header_up X-Forwarded-Proto {scheme} }}Reload Caddy:
caddy reload2. Traefik Configuration
Section titled “2. Traefik Configuration”Traefik passes custom headers through to backend services automatically.
Docker Compose with Labels
Section titled “Docker Compose with Labels”services: critalarm: image: ghcr.io/anvilnine/critalarm:latest container_name: critalarm restart: unless-stopped environment: - CRITALARM_BASE_URL=https://alerts.example.com - CRITALARM_BEHIND_PROXY=true labels: - "traefik.enable=true" - "traefik.http.routers.critalarm.rule=Host(`alerts.example.com`)" - "traefik.http.routers.critalarm.entrypoints=websecure" - "traefik.http.routers.critalarm.tls.certresolver=letsencrypt" - "traefik.http.services.critalarm.loadbalancer.server.port=8080" - "traefik.http.services.critalarm.loadbalancer.passHostHeader=true" networks: - traefik-net
networks: traefik-net: external: trueTraefik File Provider (Dynamic YAML)
Section titled “Traefik File Provider (Dynamic YAML)”If using file-based dynamic configuration:
http: routers: critalarm: rule: "Host(`alerts.example.com`)" entryPoints: - "websecure" service: critalarm tls: certResolver: letsencrypt
services: critalarm: loadBalancer: passHostHeader: true servers: - url: "http://127.0.0.1:8080"3. Nginx Configuration
Section titled “3. Nginx Configuration”Nginx needs explicit directives or it drops custom and authorization headers.
Virtual Host Configuration
Section titled “Virtual Host Configuration”server { listen 443 ssl http2; server_name alerts.example.com;
ssl_certificate /etc/letsencrypt/live/alerts.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/alerts.example.com/privkey.pem;
location / { proxy_pass http://127.0.0.1:8080; proxy_http_version 1.1;
# Standard proxy headers 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; proxy_set_header X-Forwarded-Host $host;
# Ensure all incoming request headers pass through proxy_pass_request_headers on;
# Explicitly forward Authorization header proxy_set_header Authorization $http_authorization; proxy_pass_header Authorization;
# Disable buffering for instantaneous webhook response proxy_buffering off; proxy_read_timeout 60s; proxy_send_timeout 60s; }}Check configuration and reload Nginx:
nginx -t && nginx -s reloadVerifying Header Delivery
Section titled “Verifying Header Delivery”To test that your reverse proxy correctly forwards authentication and priority headers, execute a test publish using curl:
curl -v -X POST https://alerts.example.com/prod \ -H "Authorization: Bearer tk_YOUR_TOKEN" \ -H "X-Title: Reverse Proxy Verification" \ -H "X-Priority: 5" \ -d "Proxy test message"What to Look For
Section titled “What to Look For”- HTTP 200 OK: Confirms the
Authorizationheader passed through successfully. If you receive401 Unauthorized, your proxy stripped theAuthorizationheader. "incident_id": "..."in JSON: Confirms theX-Priority: 5header was received and triggered an incident. If no incident was opened, check whether customX-headers were filtered.