Repository navigation
Commit eb68475
authored
feat(website): serve the site at nostream.dev and redirect every other host to it (#889)
* feat(website): serve the site at nostream.dev and redirect every other host to it
The site's canonical origin moves from ema.tnsor.network to https://nostream.dev.
Previews keep the preview domain: a broken PR build must never sit on the domain
that is the public face of the project.
The router worker now binds nostream.dev and www.nostream.dev alongside
ema.tnsor.network, and CANONICAL_HOST decides which host serves content:
- http -> 301 to the same path over https, for every host, preview paths
included (the proxy hop to the preview worker is already https);
- a non-canonical host -> 301 to the canonical one, keeping path and query.
Preview paths are exempt, because they are served from their own domain and a
redirect would move every preview off the origin its links name;
- everything else -> the static assets, as before.
The domain is an input rather than a constant. SITE_URL (build) and
CANONICAL_HOST (worker, passed with --var) are both set in deploy-website.yml,
so a future move is two lines instead of a search through the source, the README
and the social card.
Three defects were found by exercising the worker before writing it down, and
fixed in the same change:
1. `Response.redirect` re-encodes an already-encoded path - `/docs/a%20b` came
back as `/docs/a%2520b`, a path that does not exist. The redirect writes its
own Location from url.origin + url.pathname + url.search.
2. The first host-redirect draft ran before the preview match, so
`ema.tnsor.network/preview/888/` was 301'd to nostream.dev/preview/888/.
3. The same draft treated a port-carrying host as canonical and issued no
redirect at all; the comparison uses the bare host.
The social card is regenerated with the new domain in its footer (both PNGs,
1200x630 and 1280x640, regenerated at their own sizes). Verified: the site
builds with canonical/og:url/og:image all on https://nostream.dev and no
ema.tnsor.network left in dist/; the worker passes wrangler's bundler with
env.CANONICAL_HOST bound.
Cloudflare setup this needs, not code: the nostream.dev zone must exist, and
Always Use HTTPS on the zone makes the upgrade happen at the edge before the
worker (the worker's own 301 covers the case where it is off).
* fix(website): run the worker before assets, and keep previews off the prod host
Two review findings, both real and both verified by exercising the worker.
1. Static pages skipped the redirects. Cloudflare serves a matching static
asset before invoking the worker unless `assets.run_worker_first` is set,
and this deployment's assets are the whole site - every built route is a
file. So `www.nostream.dev/` and `ema.tnsor.network/` would have answered
200 with the page, and the host redirect would have run only on paths that
did NOT match an asset, i.e. almost never. `run_worker_first: true` puts the
worker back in front, which is what a router is.
2. Previews were reachable from the production domain. The preview branch
proxied `/preview/<n>/` from any host, so once `nostream.dev` was bound,
`https://nostream.dev/preview/<n>/` would serve a pull request's build on
the site's own address. Previews now answer only from PREVIEW_HOST; a
preview path on the canonical host is 301'd to PREVIEW_HOST, and a preview
path on www or the stale domain is proxied in place (it is not the
production host, so no PR build lands on a domain people trust).
Exercised after the change:
http -> https 301 https://nostream.dev/docs/x
http preview -> https 301 https://ema.tnsor.network/preview/888/
www -> apex (encoded path kept) 301 https://nostream.dev/docs/a%20b?y=1
stale host -> canonical 301 https://nostream.dev/about
preview on preview host 200
preview on PROD host 301 https://ema.tnsor.network/preview/888/
preview via www 200
canonical served 200
wrangler deploy --dry-run bundles with run_worker_first and CANONICAL_HOST.
* fix(website): no redirect loop with no canonical host, and compare the whole authority
Two review findings on the host layer.
1. With CANONICAL_HOST empty the layer inverted: `onCanonical` defaulted to
true, so a preview request was 301'd to the URL it was already on, forever,
and the documented "unset disables the redirects" behaviour became "unset
breaks previews". Unset now leaves the request alone, which is what
documented means.
2. The comparison used `url.hostname`, which drops the port, so
`nostream.dev:8443` counted as canonical and served the pages without
redirecting - a second origin for the same content, the thing the redirect
exists to prevent. The comparison is now against `url.host`, so a port the
config does not name is a different authority.
The first fix exposed a third defect that neither finding named: a preview on
PREVIEW_HOST was 301'd to the canonical host, whose preview branch then 301'd
it back - a two-hop loop. Restructured so exactly one rule can move a preview
off the production domain and nothing moves it anywhere else.
Exercised with and without the variable, and with a configured port:
http -> https 301 https://nostream.dev/docs/x
http preview -> https 301 https://ema.tnsor.network/preview/8/
www -> apex (encoded path kept) 301 https://nostream.dev/a%20b?y=1
stale host -> canonical 301 https://nostream.dev/about
preview host -> canonical 301 https://nostream.dev/preview/888/
prod host preview -> preview host 301 https://ema.tnsor.network/preview/888/
canonical served 200
port on canonical -> stripped 301 https://nostream.dev/about
CANONICAL unset: preview served 404 (proxied, no loop)
CANONICAL unset: page served 200
configured port: canonical served 200
configured port: default port -> redirect 301 https://nostream.dev:8443/about
* fix(website): serve previews on the preview host instead of bouncing them back
Greptile caught what my own matrix could not: the previous fix made
`ema.tnsor.network/preview/888/` redirect to `nostream.dev/preview/888/`, whose
preview rule redirects to `ema.tnsor.network/preview/888/` - a two-host
browser redirect loop. Neither hop reaches the preview worker.
The matrix reported "preview host: redirect to canonical 301 ..." as correct.
It is not: one hop is only correct if the destination serves the page, and the
destination's own rule sends it back. The check that catches this class is
walking the chain the way a browser does, not asserting a single hop - which is
what this commit adds to the reasoning, and what the evidence below records.
A preview path on PREVIEW_HOST is now served in place by the proxy branch;
every other authority still converges on the canonical host, and previews on
the production host still move to PREVIEW_HOST once, then stop there.
Chain walks, six starts, no path revisits a URL:
ema.../preview/888/ -> 404 (proxied, one hop, terminal)
ema.../preview/888/docs/x?y=1 -> 404 (query and path preserved)
http://ema.../preview/888/ -> https ema -> 404
ema.../about -> nostream.dev/about -> 200
nostream.dev/preview/888/ -> ema.../preview/888/ -> 404
nostream.dev/about -> 200
www.nostream.dev/ -> nostream.dev/ -> 2001 parent 9146fd3 commit eb68475
12 files changed
Lines changed: 135 additions & 11 deletions
File tree
- .changeset
- .github/workflows
- website/site
- public/media
- scripts
- worker
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
107 | 107 | | |
108 | 108 | | |
109 | 109 | | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
110 | 116 | | |
111 | 117 | | |
112 | 118 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
56 | 56 | | |
57 | 57 | | |
58 | 58 | | |
59 | | - | |
| 59 | + | |
60 | 60 | | |
61 | 61 | | |
62 | 62 | | |
63 | 63 | | |
64 | 64 | | |
65 | 65 | | |
66 | 66 | | |
| 67 | + | |
67 | 68 | | |
68 | 69 | | |
69 | | - | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
70 | 75 | | |
71 | 76 | | |
72 | 77 | | |
| |||
87 | 92 | | |
88 | 93 | | |
89 | 94 | | |
90 | | - | |
| 95 | + | |
91 | 96 | | |
92 | 97 | | |
93 | 98 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
34 | 34 | | |
35 | 35 | | |
36 | 36 | | |
37 | | - | |
| 37 | + | |
38 | 38 | | |
39 | 39 | | |
40 | 40 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
49 | 49 | | |
50 | 50 | | |
51 | 51 | | |
52 | | - | |
| 52 | + | |
53 | 53 | | |
54 | 54 | | |
55 | 55 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
6 | | - | |
| 6 | + | |
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
| |||
Loading
Loading
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
140 | 140 | | |
141 | 141 | | |
142 | 142 | | |
143 | | - | |
| 143 | + | |
144 | 144 | | |
145 | 145 | | |
146 | 146 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
5 | | - | |
6 | | - | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
7 | 13 | | |
8 | 14 | | |
9 | 15 | | |
10 | 16 | | |
11 | 17 | | |
| 18 | + | |
| 19 | + | |
12 | 20 | | |
13 | 21 | | |
14 | 22 | | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
15 | 26 | | |
16 | 27 | | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
17 | 42 | | |
18 | 43 | | |
19 | 44 | | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
20 | 81 | | |
21 | 82 | | |
22 | 83 | | |
| |||
0 commit comments