-
Notifications
You must be signed in to change notification settings - Fork 0
GitHub Pages Cert Renewal
This page is deprecated as of #362.
www.proton-pulse.comandstaging.proton-pulse.comare now served directly from Cloudflare Pages. There is only ONE TLS certificate in play (Cloudflare-managed, auto-renewed); the GitHub Pages origin cert /bad_authzACME dance does not exist in the new setup.Do not follow the walkthrough below unless you are executing an emergency rollback to GitHub Pages (see the Rollback to GitHub Pages section of Web-Deploy-Workflow). The page is preserved verbatim because the rollback path still needs it and no one wants to reconstruct these steps at 2am.
Retired pieces:
scripts/renew-github-pages-cert.sh-- deleted in #362.make renew-certificatetarget -- removed from Makefile.- Two-cert display on the public status page -- collapsed to a single-cert card in
js/status/main.js. The cert-monitor cron still writescert-status.jsonfor the burndown graph, now with theedgecert only.For the current cert story see the Status Page card and Web-Deploy-Workflow. What remains below is historical.
How TLS worked for www.proton-pulse.com while it was on GitHub Pages behind the Cloudflare proxy.
A request crosses two separate TLS connections, and the browser only ever sees the first:
Your browser --TLS #1--> Cloudflare edge --TLS #2--> GitHub Pages origin
| |
YE2 edge cert R13 origin cert
(valid, auto-renewed) (left expired on purpose)
- TLS #1 (browser to Cloudflare): Cloudflare presents its own Let's Encrypt cert (
YE2). Cloudflare auto-renews it, and it is the only cert a browser validates. This is whyabout:certificateshows a valid cert, and why clearing cookies or cache changes nothing: the cert is exchanged during the handshake, before any HTTP. - TLS #2 (Cloudflare to GitHub): a separate internal handshake nobody sees from outside. GitHub Pages presents its own cert for the custom domain (
R13).
So the browser padlock reflects the edge cert only. It says nothing about the origin cert.
Cloudflare SSL/TLS is pinned to explicit Full (non-strict): dashboard, proton-pulse.com zone, SSL/TLS, Overview, Configure, the option that reads "No certification validation. Accepts any certificate."
Under Full (non-strict), Cloudflare encrypts the hop to GitHub but does not validate the origin cert. That has one deliberate consequence: the GitHub Pages origin cert is allowed to expire and stay expired. It never reaches visitors, and it does not need renewing. Cloudflare keeps auto-renewing the edge cert, which is the only cert users validate. There is nothing to schedule and nothing to babysit.
Both eventually take the site down, because GitHub cannot renew the origin cert while the domain is proxied (see below), so any mode that validates the origin will fail once the cert lapses.
- Full (Strict) validates the origin cert. When the origin cert expired in July 2026, Cloudflare refused the backend and served every visitor a 526. This is the outage we had.
- Automatic SSL/TLS scans the origin and upgrades to the most secure mode it supports, so it quietly moves to Strict whenever the origin cert looks valid, then 526s when it expires. Same failure, harder to predict.
Explicit Full (non-strict) is the only stable mode for a proxied GitHub Pages site. Do not renew the origin cert to "fix" this either: a freshly valid origin cert makes Automatic or a strict setting start validating again, and the 526 returns ~90 days later.
Full (non-strict) is a deliberate tradeoff. The browser-to-Cloudflare hop is fully secure: valid cert, validated, encrypted. The Cloudflare-to-GitHub hop is still encrypted but not authenticated, so an active man-in-the-middle positioned between Cloudflare and GitHub could substitute content. For this site that risk is low: it would take active MITM on the backbone between two major clouds, and the content on that hop is public static files with no secrets (auth and database traffic go to Supabase over their own validated TLS).
We accept it because a proxied GitHub Pages origin cannot do validated (strict) origin TLS at all: GitHub does not allow a custom or Cloudflare Origin CA cert, and cannot renew its own while proxied.
The real way to close the gap without giving up Cloudflare (DDoS, CDN, WAF) is to move hosting off GitHub Pages onto Cloudflare Pages. Then Cloudflare serves the site directly, there is no untrusted origin hop, TLS is validated end to end, and this whole page stops being necessary. That is a migration of the deploy pipeline, tracked separately, not a dashboard setting.
GitHub renews the origin cert through a Let's Encrypt ACME HTTP-01 challenge: Let's Encrypt requests http://www.proton-pulse.com/.well-known/acme-challenge/..., which must reach GitHub directly. With the Cloudflare proxy on (orange cloud), Cloudflare intercepts the challenge, GitHub never answers it, and GitHub parks the cert in https_certificate.state = "bad_authz" ("the ACME authorization is in a bad state, we need to start over"). Under our configuration this is expected and harmless, which is why we leave it.
Local diagnostic, read-only:
make check-certIt prints the edge cert, the origin cert, and GitHub's ACME state. Expect the edge cert Valid and the origin cert Expired with bad_authz. That is the normal, healthy state for this setup.
From outside:
curl -sI https://www.proton-pulse.com/version.json | head -1 # expect HTTP/2 200.github/workflows/cert-monitor.yml runs scripts/cert-monitor.sh every 6 hours and writes cert-status.json plus an append-only cert-history.json to gh-pages. It records both certs (edge by connecting to the domain, origin by connecting to a GitHub Pages IP with SNI) and GitHub's Pages ACME state. The GitHub read uses the built-in Actions GITHUB_TOKEN, so there is no PAT and nothing to rotate.
The status page cert card reads those files: the edge cert drives the headline and the burndown graph, and the origin cert and ACME state show as a muted operator line. The expiry dates are public, so all of it is on the public status page.
-
scripts/cert-monitor.sh,scripts/check-cert.sh-- the probe and the local diagnostic. -
js/status/main.js-- status page cert card (renderCertCard,renderCertBurndown). -
workers/edge-status/index.js-- the separate site probe (classifySiteStatushandles 525/526). -
scripts/renew-github-pages-cert.sh-- kept for reference only; not used under the current configuration. -
Restore-Runbook.md-- full disaster recovery.