CI / typecheck + build (turbo) (push) Canceled after 0s
- 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.
57 lines
2.0 KiB
Markdown
57 lines
2.0 KiB
Markdown
---
|
|
id: cloudflare-525-writeup
|
|
title: Diagnosing Cloudflare 525 (Origin TLS Handshake Failed)
|
|
type: writeup
|
|
tags:
|
|
- cloudflare
|
|
- tls
|
|
- 525
|
|
- caddy
|
|
- debugging
|
|
status: published
|
|
author: asep
|
|
created_at: 2026-08-20
|
|
updated_at: 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)
|
|
|
|
1. **No Caddy site block for the SNI.** Cloudflare proxies every `*.asepharyana.my.id`
|
|
record. A host without a matching Caddy `site` block has no certificate, so the
|
|
handshake dies. This is the #1 cause and the one that bit `wiki` and `mcp`.
|
|
2. **Certificate still provisioning.** The first request after adding a block triggers
|
|
ACME `http-01` issuance. Until the cert lands (~10s), the origin 525s. Self-heals.
|
|
3. **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.
|