- 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.
2.0 KiB
id, title, type, tags, status, author, created_at, updated_at
| id | title | type | tags | status | author | created_at | updated_at | |||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| cloudflare-525-writeup | Diagnosing Cloudflare 525 (Origin TLS Handshake Failed) | writeup |
|
published | asep | 2026-08-20 | 2026-08-20 |
Diagnosing Cloudflare 525 (Origin TLS Handshake Failed)
A 525 appears between the user and the origin when Cloudflare (Strict TLS mode) cannot complete the TLS handshake to the origin server. This writeup captures the debugging loop that recurred while wiring new subdomains.
Symptom
curl https://<sub>.asepharyana.my.id → HTTP/2 525. Browser shows Cloudflare's
"SSL handshake failed" page.
Root causes (in order of likelihood)
- No Caddy site block for the SNI. Cloudflare proxies every
*.asepharyana.my.idrecord. A host without a matching Caddysiteblock has no certificate, so the handshake dies. This is the #1 cause and the one that bitwikiandmcp. - Certificate still provisioning. The first request after adding a block triggers
ACME
http-01issuance. Until the cert lands (~10s), the origin 525s. Self-heals. - Wrong cert presented. Rare here — Caddy serves the SNI-matched cert; a mismatch means the block points at the wrong backend or the cert store is stale.
The debugging loop
curl -sI https://<sub>/ # 525?
grep -n "<sub>" /etc/caddy/Caddyfile # block present?
sudo journalctl -u caddy | grep -i "tls\|acme\|<sub>" # cert issued?
openssl s_client -connect 127.0.0.1:443 -servername <sub> # origin cert valid?
If the block is missing: add import proxy <port>, caddy validate, systemctl reload caddy. If the cert is mid-issuance: wait and re-test. Do not point Cloudflare
at a non-existent origin or set the SSL mode to Flexible — Flexible mode breaks already-
working Strict setups.
Lesson
Every new subdomain needs (a) a Caddy site block and (b) a Cloudflare DNS record that proxies to the origin. Omit either and you get a 525. The wildcard DNS means you only add the Caddy side.