# Traefik infrastructure (epicnabbo.nl) This directory mirrors the live Traefik dynamic configuration used to proxy `epicnabbo.nl` (and subdomains) on the production host. The dynamic config lives on the server at `/docker/proxyserver/dynamic/`. ## Fix: HTTP 525 / broken layout (origin TLS chain) The site returned **HTTP 525 (Cloudflare SSL handshake failed)** for every asset, which broke the whole UI (JS/CSS chunks, the Nitro client, the toolbar, the "Enter Hotel" button, etc.). Root cause: Traefik's ACME resolver stored the `epicnabbo.nl` leaf certificate in `/letsencrypt/acme.json` **without** the Let's Encrypt intermediate. When Cloudflare connects to the origin in "Full (strict)" mode it cannot build the certificate chain and aborts the TLS handshake → 525. Fix: the `epicnabbo` router serves a **file-based certificate** that includes the full chain (leaf + LE YR2 intermediate), configured in `dynamic/epicnabbo-tls.yml` and referenced from `dynamic/epicnabbo.nl.yml` (`tls: {}` enables TLS on the router so the SNI matches the file cert). ### Files - `dynamic/epicnabbo.nl.yml` — router/service/serversTransport for epicnabbo.nl. - `dynamic/epicnabbo-tls.yml` — file-based certificate (leaf + intermediate). - `regenerate-epicnabbo-chain.sh` — rebuilds the full chain from `acme.json`. ### NOT committed (contain secrets) - `epicnabbo-fullchain.pem` — leaf + intermediate. - `epicnabbo-key.pem` — private key. ### Re-generating the chain (e.g. after cert renewal, before 2026-10-03) ```bash ./infra/traefik/regenerate-epicnabbo-chain.sh docker restart traefik ``` The `epicnabbo.nl` entry must be **absent** from `acme.json` so Traefik does not prefer the (incomplete-chain) ACME certificate over the file certificate.