Repository navigation
feat(seo): add per-page descriptions and canonical metadata - #1531
Merged
BenjaminMichaelis merged 3 commits intoOct 17, 2026
Merged
Conversation
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Static-page metadata tests expect production URLs but the default test configuration supplies a localhost origin.
1 open issue
What changed in this PR
Adds page-specific SEO metadata while preserving parser-generated chapter canonicals.
Changes:
- Adds public-page descriptions and optional generated chapter descriptions with heading fallbacks.
- Shares descriptions across metadata formats and aligns Open Graph URLs with canonicals.
- Adds metadata tests and documents the contract.
| File | Description |
|---|---|
| README.md | Documents metadata selection and fallbacks. |
| EssentialCSharp.Web/Views/Shared/_Layout.cshtml | Renders descriptions and canonical metadata. |
| EssentialCSharp.Web/Services/ChapterDescriptionService.cs | Loads generated descriptions with heading fallbacks. |
| EssentialCSharp.Web/Program.cs | Registers the description service. |
| EssentialCSharp.Web/Controllers/McpSetupController.cs | Adds the setup-page description. |
| EssentialCSharp.Web/Controllers/HomeController.cs | Sets descriptions and preserves chapter canonicals. |
| EssentialCSharp.Web.Tests/FunctionalTests.cs | Checks rendered descriptions and canonicals. |
| EssentialCSharp.Web.Tests/ChapterDescriptionServiceTests.cs | Tests generated descriptions and fallbacks. |
Review effort: Balanced
Re-base parser chapter canonicals onto SiteSettings:BaseUrl, keeping the primary page path for alias routes, and strip the parser canonical so the layout renders exactly one canonical/og:url. Pin BaseUrl in metadata tests and cover dev.essentialcsharp.com. Supersedes the request-Host approach from #1130 without enabling X-Forwarded-Host.
BenjaminMichaelis
deleted the
benjaminmichaelis/per-page-meta-descriptions
branch
October 17, 2026 04:36
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Why
Every page currently receives the same site-wide description, and
og:urlalways points at the homepage. This makes chapter and static-page metadata misleading to search engines and link previews. It also has to be correct on bothessentialcsharp.comanddev.essentialcsharp.com.What changed
<link>and a matchingog:urlon every successful, non-Identity page, excluding query strings. The origin always comes fromSiteSettings:BaseUrl./hello-worldpoint to/introducing-c), re-based from the parser's hardcoded production origin onto the configured origin.Supersedes #1130
This reconciles #1130 and addresses its review findings:
X-Forwarded-Hostwithout an allowlist: avoided. The requestHostis not used for canonicals, soXForwardedHostis not enabled. That flag would also have let a trusted proxy poison emailed reset/confirmation links.XForwardedHostis a no-op underASPNETCORE_FORWARDEDHEADERS_ENABLED=true: moot for the same reason.Also addresses this PR's own review comment: metadata tests now pin
SiteSettings:BaseUrlinstead of depending onappsettings.Development.json.Deployment note
Each environment must set
SiteSettings__BaseUrlto its own origin, for examplehttps://dev.essentialcsharp.comfor dev. Production already depends on this for the www→apex redirect.Validation
The full Web test suite passes locally (186 tests), built with
/p:SkipFrontendBuild=true. The local frontend build is blocked by an unrelated stale@zxcvbn-ts/coreinstall.