# Reverse proxy & TLS

Source: https://www.pingtower.com/docs/self-hosting/proxy/

The daemon serves plain HTTP on localhost. One line of Caddy, or a small nginx block, puts a certificate in front of it.

## Caddy

`/etc/caddy/Caddyfile`:

```text
alerts.example.com {
    reverse_proxy 127.0.0.1:8391
}
```

Caddy issues and renews the certificate itself. Nothing else is needed.

## nginx

```nginx
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:8391;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
        proxy_read_timeout 60s;
    }
}
```

<div class="docs-callout warn"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M8 2 1.5 13.5h13L8 2Z"/><path d="M8 6.5v3M8 11.5v.5"/></svg><div><span class="t">Long-poll needs a patient proxy</span><code>GET /v1/pull?wait=25</code> holds the connection open for up to 25 seconds. A proxy read timeout under 30 seconds turns every quiet poll into a 504. Caddy&rsquo;s default is fine; nginx&rsquo;s default of 60 seconds is fine; a stricter default in front of either is not.</div>
</div>


## Behind Cloudflare

Works as-is. Set SSL mode to Full (strict) and give the origin a Cloudflare origin certificate, which is how api.pingtower.com itself runs. Keep the proxy's real-IP handling on if you rate-limit by address.

## Check it

```sh
curl -s https://alerts.example.com/healthz
# {"version":"…","tenants":1}
```
