Files
mcpedia/content/docs/caddy/reverse-proxy.md
T
asepharyana 53d636af0e
CI / typecheck + build (turbo) (push) Canceled after 0s
feat(phase7): grow corpus, MCP write-tools+auth, /metrics observability
- content/: +5 real docs (caddy, bullmq, mcp-streamable-http, postgres-fts,
  cloudflare-525 writeup) across docs/writeups/notes. Reindexed: 9 docs, 17
  chunks, 5 revisions (was 4 docs).
- apps/mcp: add write-tools index_document/reindex_all/restore_revision (require
  x-webhook-secret) + queue_status (public). createMcpServer(authSecret?) threads
  the HTTP header; stdio keeps writes open (trusted local).
- apps/api: GET /metrics (Prometheus text: uptime + queue job gauges).
- Caddy: expose /metrics on wiki. domain -> :4020.
Verified live: /metrics 200; MCP tools/list -> 10; index_document unauth -> error,
auth -> enqueues + worker drains; typecheck green.
2026-08-20 10:04:16 +07:00

2.3 KiB
Raw Blame History

id, title, type, tags, status, author, created_at, updated_at
id title type tags status author created_at updated_at
caddy-reverse-proxy Caddy Reverse Proxy documentation
caddy
reverse-proxy
tls
infra
published asep 2026-08-20 2026-08-20

Caddy Reverse Proxy

Caddy is the single reverse proxy on the host. Every public service sits behind it and terminates TLS with automatic Let's Encrypt certificates. Understanding its config model prevents the two recurring failure modes: 525 (origin TLS) and 404 (path routing).

Config model

The live config is /etc/caddy/Caddyfile. It is a manual, tuned file — CI does not deploy it, so the live file is the source of truth and must be kept in sync with any repo reference.

A reusable snippet handles the common case:

(proxy) {
    encode zstd gzip
    header {
        -Server
        X-Content-Type-Options "nosniff"
    }
    reverse_proxy 127.0.0.1:{args[0]} {
        transport http {
            keepalive 120s
            dial_timeout 3s
        }
    }
}

A site block wires a domain to a backend port:

wiki.asepharyana.my.id {
    import proxy 4016
}

Path routing: handle vs handle_path

handle /trpc/* forwards the request with the /trpc prefix preserved. handle_path /trpc/* strips it before proxying. Stripping is wrong when the upstream already mounts the route at /trpc — the upstream then receives / and 404s.

Rule: when the upstream already serves the path (e.g. Hono app.all("/trpc/*")), use handle, not handle_path.

525 — origin TLS handshake failed

Cloudflare proxies every *.asepharyana.my.id record. If a subdomain has no Caddy site block, Caddy has no certificate for that SNI and the TLS handshake dies → Cloudflare returns 525. Fix: add the block, caddy validate, systemctl reload caddy. The first request after adding a block triggers ACME certificate issuance; until it completes the origin may briefly 525. That is expected and self-heals in ~10s.

Slow upstreams

LLM gateways (9router) have time-to-first-token of 30–40s. The default response_header_timeout 30s yields false 504s. Lengthen it for those blocks:

reverse_proxy 127.0.0.1:4014 {
    transport http {
        response_header_timeout 120s
        read_timeout 300s
        write_timeout 300s
    }
}