Skip to content

fix(seo): enforce trailing slashes on internal links to fix 134 non-indexable canonicals - #19004

Draft
sfanahata wants to merge 1 commit into
masterfrom
fix/non-indexable-canonicals
Draft

fix(seo): enforce trailing slashes on internal links to fix 134 non-indexable canonicals#19004
sfanahata wants to merge 1 commit into
masterfrom
fix/non-indexable-canonicals

Conversation

@sfanahata

Copy link
Copy Markdown
Contributor

DESCRIBE YOUR PR

Fixes 134 non-indexable canonical issues where pages point their canonical tag to a URL that triggers a redirect (missing trailing slash → trailing slash), creating broken canonical chains that confuse Google's indexation.

Root cause

The site is configured with trailingSlash: true (in both next.config.ts and vercel.json), meaning all canonical URLs include a trailing slash. However, hundreds of internal links in content files were authored without trailing slashes. When followed, these trigger a 308 redirect to the trailing-slash version, creating a chain between the link and the canonical URL that degrades SEO signals.

Changes

1. SmartLink trailing slash normalization (src/components/smartLink.tsx, src/utils.ts)

  • Added ensureTrailingSlash() utility to src/utils.ts (alongside the existing stripTrailingSlash())
  • Applied it in SmartLink for all internal page links at render time
  • Skips mailto: and other scheme-based URLs, and static file paths with extensions (e.g. .pdf)
  • This prevents future regressions — any link authored without a trailing slash is automatically normalized before rendering
  • Preserves hash fragments (#section) and query strings (?param=value)
  • Added 10 unit tests in src/utils.test.ts

2. Content link cleanup (366 files across docs/, includes/, platform-includes/)

3. MD export canonical URLs (scripts/generate-md-exports.mjs)

  • Fixed canonical URL construction to include trailing slashes, matching the behavior of generateMetadata() in page.tsx and mdToCanonicalPath() in middleware.ts

4. Deduplication (app/sitemap.ts)

  • Replaced the inline appendSlash() helper with the shared ensureTrailingSlash() from src/utils.ts

Verification

  • All 234 tests pass (including 10 new ensureTrailingSlash tests)
  • ESLint: 0 errors
  • TypeScript: compiles clean
  • Prettier: all files formatted

IS YOUR CHANGE URGENT?

  • Urgent deadline (GA date, etc.)
  • Other deadline
  • None: Not urgent, can wait up to 1 week+

PRE-MERGE CHECKLIST

  • Checked Vercel preview for correctness, including links
  • PR was reviewed and approved by any necessary SMEs (subject matter experts)
  • PR was reviewed and approved by a member of the Sentry docs team

@vercel

vercel Bot commented Aug 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
develop-docs Ready Ready Preview Aug 10, 2026 7:16pm
sentry-docs Ready Ready Preview Aug 10, 2026 7:16pm

Request Review

@codeowner-assignment
codeowner-assignment Bot requested review from a team August 10, 2026 18:48

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit f42310b. Configure here.

const hasScheme = realTo.includes(':');
const hasFileExtension = /\.\w{2,10}(?=[?#]|$)/.test(realTo);
const normalizedHref =
hasScheme || hasFileExtension ? realTo : ensureTrailingSlash(realTo);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extension check scans query and hash

Medium Severity

hasFileExtension runs against the full href, including query strings and hashes. A page link whose query or fragment ends in a file-like suffix (for example .js or .json) is treated as a static file and skips ensureTrailingSlash, so those internal links still miss the trailing slash and can hit the same redirect/canonical issue this PR aims to fix.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit f42310b. Configure here.

Comment thread scripts/generate-md-exports.mjs Fixed
Comment thread scripts/generate-md-exports.mjs Fixed
Comment thread scripts/generate-md-exports.mjs Fixed
…ndexable canonicals

Three changes to eliminate broken canonical chains caused by missing
trailing slashes:

1. SmartLink normalization: Add ensureTrailingSlash() to src/utils.ts
   and apply it in SmartLink for all internal links. This prevents
   future regressions by normalizing URLs at render time, avoiding
   308 redirects that break canonical signal chains.

2. Content link cleanup: Fix 732 internal links across 366 content
   files (docs/, includes/, platform-includes/) that were missing
   trailing slashes on markdown links and PlatformLink to= props.

3. MD export canonical URLs: Fix generate-md-exports.mjs to include
   trailing slashes in canonical URLs embedded in export HTML,
   matching the behavior of generateMetadata() and middleware.

Also consolidates the duplicated appendSlash() helper in sitemap.ts
to use the shared ensureTrailingSlash() utility.
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.

JavaScript docs don't reference the right version

2 participants