Skip to content

Give pages without a slug their own canonical URL - #192

Merged
shigahi merged 1 commit into
masterfrom
unmapped-page-canonical
Oct 28, 2026
Merged

shigahi merged 1 commit into
masterfrom
unmapped-page-canonical

Conversation

@shigahi

@shigahi shigahi commented Oct 28, 2026

Copy link
Copy Markdown
Collaborator

Summary

Closes #190.

Pages that aren't mapped to a slug (opened at /<page-id> or /<Title>-<page-id>) were rewritten with the root slug, both in the worker and in the client script. As a result, they:

  • declared the home page as their canonical URL and og:url, so search engines could treat them as duplicates of the home page;
  • took over the home page's per-page metadata (PAGE_METADATA['']): title, description, OG image and hreflang alternates.

Changes:

  • The worker passes slug = null for these pages, plus their canonical path: /<page-id>, so /Title-<id> and /<id> share one canonical URL. When the path has no page ID, the request path is used. appendJavascript builds the canonical URL once and hands it to MetaRewriter and HeadRewriter.
  • A new pageMetadata(slug) returns no per-page metadata for null. It checks for null explicitly, so a slug literally named "null" can't leak its metadata.
  • No generated OG image is added for these pages. /og-image/ only renders pages with a slug, so they previously got the home page's image. The default OG image still applies.
  • The client script's applyMeta uses null the same way, so the home page's title no longer replaces the page's own title after load.
  • Mapped pages, mapped pages opened by page ID, the home page and the custom 404 page behave as before.

Verification

  • npm test: 25 tests pass. Three tests are new. They run the worker's real head, meta and title handlers on fake elements, because HTMLRewriter isn't available in Node. Against the old code, only the test for pages without a slug fails; the other two guard against regressions.

  • In wrangler dev against the live render check page, with PAGE_METADATA[''] titled "Home title" and the unmapped database item Item Beta:

    Old worker New worker
    Crawler (GPTBot), /<id> or /Item-Beta-<id> title "Home title", canonical and og:url set to the home page title "Item Beta | Notion", canonical and og:url set to /<id>
    Chromium, after the Notion client loads document.title "Home title" document.title "Item Beta"
    Home page unchanged unchanged

Note

In the browser, the Notion client rewrites link[rel=canonical] and og:url to its own /Title-<id> path after it loads, on every page including the home page. This happened before this PR too. Since #191, crawlers that don't run JavaScript get the server-rendered page, where the worker's canonical URL is what counts. Keeping the canonical URL in applyMeta as well would be a separate change.

Pages that aren't mapped to a slug were rewritten with the root slug,
so they declared the home page as canonical URL and og:url and took
over its per-page metadata, both in the worker and in the client
script. Search engines could treat them as duplicates of the home page.

Use the page ID path as their canonical URL (so /Title-<id> and /<id>
share one), and apply no per-page metadata or generated OG image to
them.

Closes #190

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@shigahi
shigahi merged commit cf3fe98 into master Oct 28, 2026
5 checks passed
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.

Unmapped pages declare the home page as canonical URL

1 participant