Skip to content

feat(proxy): branded 404 for unknown hosts behind admin gate - #97

Merged
dviejokfs merged 2 commits into
mainfrom
feat/branded-proxy-404
May 26, 2026
Merged

dviejokfs merged 2 commits into
mainfrom
feat/branded-proxy-404

Conversation

@dviejokfs

Copy link
Copy Markdown
Contributor

Summary

Replaces the bare <h1>404 - Not Found</h1> returned by the admin-gate path with a self-contained branded HTML page so users hitting a host without a deployed app (e.g. mid-DNS-propagation) see a Temps page instead of something indistinguishable from nginx/haproxy.

Reproducible today: curl -i https://app1.temps.kfs.es/ → 50-byte generic HTML. With this PR: a dark-mode branded page with the Temps t mark, a DEPLOYMENT_NOT_FOUND · 404 chip, the host, the request ID for support correlation, and a docs link.

Scope

Only the admin-gate denial site in crates/temps-proxy/src/proxy.rs (around line 3275). The static-file-not-found path inside a real deployment, and other internal 404s, are intentionally untouched in this PR — they live inside an app's own context where a custom 404 would be more appropriate.

Implementation

  • New crates/temps-proxy/src/branded_404.rs module with:
    • escape(&str) -> String HTML-escape helper (&<>"' → entities)
    • render(host, request_id) -> String returning a complete HTML document
  • Zero external assets — no remote fonts, scripts, or images. The page must work for hosts that haven't resolved DNS yet.
  • Reflected XSS defense: Host header (attacker-controlled) and request_id are escaped before interpolation.
  • meta robots noindex,nofollow so misconfigured domains don't pollute search results.
  • Module registered in crates/temps-proxy/src/lib.rs.
  • proxy.rs call site updated to use branded_404::render(&ctx.host, &ctx.request_id) — headers (Cache-Control, X-Request-ID, Content-Type, Content-Length) and routing_status = \"admin_gate_denied\" preserved.

Test plan

  • cargo check --lib -p temps-proxy — passes (1 pre-existing workspace-profile warning, unrelated)
  • cargo test --lib -p temps-proxy branded_404 — 6 tests pass:
    • escape_handles_html_metacharacters
    • render_embeds_host_and_request_id
    • render_escapes_malicious_host (verifies <img onerror=...> payload is escaped)
    • render_escapes_quotes_in_request_id
    • render_is_self_contained_no_external_assets (asserts only two https URLs: docs link + footer)
    • render_marks_noindex
  • Manual: after deploy, curl -i https://app1.temps.kfs.es/ returns 200-byte+ HTML with the brand mark, host name, and request ID.

Notes

  • Page is plain HTML (no scripts), so it works under strict CSPs and on no-JS clients.
  • Dark by default; respects prefers-color-scheme: light.
  • No new dependencies.

dviejokfs added 2 commits May 26, 2026 07:51
When the admin gate is enabled and a client IP that isn't allow-listed
hits a host without a deployed app, the proxy used to return a
50-byte `<h1>404 - Not Found</h1>` body — indistinguishable from
nginx/haproxy and confusing for users mid-DNS-propagation.

Render a self-contained dark-mode HTML page instead: Temps "t" brand
mark, `DEPLOYMENT_NOT_FOUND · 404` chip, the requested host, the
request ID for support correlation, and a link to the domains docs.

- New `branded_404` module in `temps-proxy` with an HTML-escape helper
  and `render(host, request_id) -> String`. Zero external assets (no
  fonts, scripts, or images) so the page works for hosts that still
  can't resolve other origins.
- Reflected XSS defense: host (attacker-controlled via `Host:` header)
  and request_id are HTML-escaped before interpolation.
- `meta robots noindex,nofollow` so misconfigured domains don't
  pollute search results.
- Tests cover happy path, escape correctness, malicious host, quote
  escaping in request_id, no external asset references, and noindex.
@dviejokfs
dviejokfs force-pushed the feat/branded-proxy-404 branch from 2ebdb97 to 944916f Compare May 26, 2026 05:52
@dviejokfs
dviejokfs merged commit ce763ee into main May 26, 2026
6 checks passed
@dviejokfs
dviejokfs deleted the feat/branded-proxy-404 branch June 5, 2026 11:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant