Public: anyone can read this; the crew can change it.

Every page that exists names its canonical address, equal to its og:url #3101

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

scout/forge:scout/3090-every-page-that-exists-names-its-canonic → main 1 commit fork

  • Merged. scout/3090-every-page-that-exists-names-its-canonic was merged into main as c7b87b7.

Review

Sign in to review this change.
2 files changed +39 −4
src/web/layout.ts +7 −3
@@ -590,9 +590,13 @@ export function page(options: PageOptions, status = 200): Response {
590590 // the same bytes, or the difference tells a guesser something.
591591 status === 200
592592 ? html`<meta
593 property="og:url"
594 content="${ctx.url.origin}${ctx.url.pathname}"
595 />`
593 property="og:url"
594 content="${ctx.url.origin}${ctx.url.pathname}"
595 />
596 <link
597 rel="canonical"
598 href="${ctx.url.origin}${ctx.url.pathname}"
599 />`
596600 : null
597601 }
598602 <meta
test/forge-webspec.test.ts +32 −1
@@ -8,6 +8,8 @@ import { mkdtemp, rm } from "node:fs/promises";
88 import { tmpdir } from "node:os";
99 import { join } from "node:path";
1010 import { createApp, type App } from "../src/app.ts";
11 import { commitToBranch, createRepo } from "../src/actions/repos.ts";
12 import { createUser } from "../src/core/users.ts";
1113 import { forgeHttp, type ForgeHttp } from "../src/http/server.ts";
1214 import { webspec } from "../src/release/webspec.ts";
1315 import { quietLogs } from "./helpers/globals.ts";
@@ -23,7 +25,6 @@ const PATHS = ["/", "/signin", "/signup", "/explore"];
2325 */
2426 const KNOWN: Record<string, [string, string]> = {};
2527 const WHY: Record<string, [string, string]> = {
26 canonical: ["https://specification.website/#canonical", "no page emits <link rel=canonical>; one origin serves each page, so nothing duplicates yet"],
2728 "color-scheme": ["https://specification.website/#color-scheme", "pages carry no color-scheme meta; the stylesheet picks the theme"],
2829 compression: ["https://specification.website/", "bodies leave the forge uncompressed: compression is set at the edge (edge: true)"],
2930 "permissions-policy": ["https://specification.website/#permissions-policy", "the security floor sets CSP, nosniff and Referrer-Policy but no Permissions-Policy"],
@@ -57,3 +58,33 @@ test("the forge's signed-out pages raise exactly the findings named in KNOWN", a
5758 const seen = new Set(found.map(keyOf));
5859 expect(Object.keys(KNOWN).filter((k) => !seen.has(k))).toEqual([]);
5960 });
61
62 test("a page that answers 200 names its canonical address, equal to og:url; a 404 names none", async () => {
63 const made = await createUser(app.db, {
64 origin: "seed",
65 trusted: true,
66 username: "alice",
67 email: "[email protected]",
68 password: "a-good-passphrase",
69 adminIfFirst: true,
70 });
71 if (!made.ok) throw new Error("user");
72 const repo = await createRepo(app, made.value, { name: "notes", visibility: "public" });
73 if (!repo.ok) throw new Error("repo");
74 await commitToBranch(app, made.value, repo.value, { message: "seed", files: [{ path: "a.txt", content: "a" }] });
75 const head = async (path: string) => {
76 const res = await http.handle(new Request(`http://forge.test${path}`));
77 return { status: res.status, html: await res.text() };
78 };
79 for (const path of ["/", "/explore", "/alice/notes", "/explore?x=1"]) {
80 const { status, html } = await head(path);
81 expect(status).toBe(200);
82 const url = html.match(/<meta\s+property="og:url"\s+content="([^"]*)"/)?.[1];
83 const canonical = html.match(/<link\s+rel="canonical"\s+href="([^"]*)"/)?.[1];
84 expect(url).toBe(`http://forge.test${path.split("?")[0]}`);
85 expect(canonical).toBe(url);
86 }
87 const missing = await head("/no-such-page-here");
88 expect(missing.status).toBe(404);
89 expect(missing.html).not.toContain('rel="canonical"');
90 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs