feat(deploy/site): serve the apex from the same Pages project, no redirect rule

Vercel was doing the apex->www 308 itself (its "redirect to www" project
setting), so nothing about Cloudflare requires a redirect — the behaviour
just disappears with Vercel. Rather than rebuild it with a zone Redirect
Rule plus a proxied placeholder record, attach pcbjam.com as a SECOND
custom domain on pcbjam-site. Both hosts serve the site and the pages
already emit canonical=www, which is what consolidates them for search.

That drops the riskiest artefact in the migration. Redirect Rules are
zone-scoped and run BEFORE Workers/Pages routing, so a `contains` match
instead of `eq` would 308 app./editor./demo./api. to www — breaking the
product API, not just a marketing page. The sibling hosts are also the
reason this was worth avoiding rather than merely guarding.

APEX_MODE (lib/common.sh) selects the topology, defaulting to `serve`.
08-verify-prod.sh now dispatches through assert_apex: in serve mode it
requires the apex to answer 200 with no hop, to not be a stale Vercel
response, to declare canonical=www, and to expose /api/waitlist. The
`redirect` mode and 07's rules/apex phases are kept for the alternative.

08 also checks the attached domains via wrangler rather than the REST API,
so the whole serve-mode path needs only `wrangler login` — no zone scopes
at all.

Comments that explained themselves via the old redirect are corrected:
astro.config.mjs, web/standalone/src/lib/config.ts and
scripts/deploy/build-demo.mjs. The demo keeps posting to www — not because
the apex redirects, but because a CORS preflight cannot follow one, so
aiming at a host that might ever redirect is a latent breakage.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LAmkjM7okPdScp9XLW1JVr
This commit is contained in:
Viktor Vaczi 2026-07-27 14:34:16 +02:00
commit df38ebafb7
7 changed files with 138 additions and 50 deletions

View file

@ -262,7 +262,62 @@ assert_parity() {
echo "parity: PASS"; return 0
}
# Apex -> www redirect, path + query preserved. Prod only.
# Dispatch on the chosen apex topology (see APEX_MODE in lib/common.sh).
assert_apex() {
case "${APEX_MODE:-serve}" in
serve) assert_apex_serves "${1:-$APEX_BASE}" ;;
redirect) assert_apex_redirect "${1:-$APEX_BASE}" ;;
*) die "APEX_MODE must be 'serve' or 'redirect' (got '${APEX_MODE}')" ;;
esac
}
# APEX_MODE=serve: the apex is its own Pages custom domain. It must answer 200
# directly (no hop), and it must still declare www as canonical — that tag is the
# only thing consolidating the two hostnames for search, since we are deliberately
# not redirecting.
assert_apex_serves() {
_apex="${1:-$APEX_BASE}"
section "apex serves directly: $_apex (APEX_MODE=serve)"
_rc=0
_t="$(_trace "$_apex/")"
_st="$(printf '%s' "$_t" | cut -f2)"; _hops="$(printf '%s' "$_t" | cut -f3)"
if [ "$_st" = "200" ]; then
echo "PASS $_apex/ -> 200 (hops $_hops)"
else
echo "FAIL $_apex/ -> $_st (expected 200; is the apex attached as a custom domain?)"; _rc=1
fi
_h="$(_headers "$_apex/")"
if [ -n "$(_hdr "$_h" x-vercel-id)" ]; then
echo "FAIL x-vercel-id present on the apex — still Vercel (stale DNS, not a pass)"; _rc=1
else
echo "PASS no x-vercel-id on the apex"
fi
_can="$($CURL -L "$_apex/" 2>/dev/null | tr -d '\n' \
| sed -n 's/.*rel="canonical" href="\([^"]*\)".*/\1/p' | head -1)"
case "$_can" in
https://www.pcbjam.com/*|https://www.pcbjam.com)
echo "PASS apex declares canonical $_can" ;;
*)
echo "FAIL apex canonical is '${_can:-absent}' — must point at www, otherwise the"
echo " two hostnames compete instead of consolidating"; _rc=1 ;;
esac
# The demo cross-posts to www, but if anyone ever points it at the apex, the
# endpoint has to be reachable there too. Cheap to confirm.
_pre="$($CURL -o /dev/null -w '%{http_code}' -X OPTIONS "$_apex/api/waitlist" \
-H 'Origin: https://demo.pcbjam.com' \
-H 'Access-Control-Request-Method: POST' 2>/dev/null || true)"
[ "$_pre" = "204" ] && echo "PASS apex /api/waitlist preflight 204" \
|| { echo "FAIL apex /api/waitlist preflight $_pre"; _rc=1; }
return $_rc
}
# APEX_MODE=redirect: apex -> www, path + query preserved. Also used by
# 00-baseline.sh, since that is what Vercel does today.
assert_apex_redirect() {
_apex="${1:-$APEX_BASE}"
section "apex redirect: $_apex"