Repository navigation
fix(seo): use Bolt's canonical URL as the og:url fallback - #63
Merged
Fredxd merged 1 commit intoOct 23, 2026
Merged
Conversation
When no override, SEO field or `default.canonical` sets a canonical, Seo::canonical() returned the raw request URI. og:url and twitter:url therefore ignored `remove_default_locale_on_canonical` and `general/canonical`, kept the query string, and disagreed with the <link rel="canonical"> that Bolt renders. Fall back to Bolt's public `canonical()` Twig function instead, which is what Bolt's own canonical link uses. Bolt's Canonical service is private, so it can't be fetched from the container at extension initialization. The request URI is kept as the last resort for requests without a matched route. Fixes AppoloDev#32
Vondry
force-pushed
the
fix/canonical-respect-bolt-settings
branch
from
October 23, 2026 12:49
3da6257 to
e6225a9
Compare
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.
Fixes #32
Summary
When nothing sets a canonical explicitly,
Seo::canonical()fell back to the raw request URL ($request->getUri()). That value feedsog:urlandtwitter:url, so those tags:localization.remove_default_locale_on_canonical(this is what remove_default_locale_on_canonical option is ignored #32 reports),general/canonicalhost,/page/foo?page=2&utm_source=newsletter,<link rel="canonical">that Bolt itself renders on the same page.Change
The final fallback now uses Bolt's public
canonical()Twig function (Bolt\Twig\HtmlExtension::canonical()). This is the sameBolt\Canonicalservice that renders Bolt's<link rel="canonical">.Seoalready holds the Twig environment, so it calls the function through that. TheBolt\Canonicalservice itself is private, and in a real install it isn't available from the container when the extension initializes. The request URL is kept as a last resort for requests without a matched route.The resolution order is otherwise unchanged:
override_default.<route>.canonicaldefault.canonicalBackwards compatibility
default.canonical) are unaffected. I verified this on a real site.og:urlandtwitter:urlnow match<link rel="canonical">when no canonical was set explicitly. Anything Bolt puts in its canonical URL now appears inog:urlas well, for example?order=…&status=…on listing pages, which bolt/core#3735 removes.Manual verification (Bolt 6.1.9)
I tested this on a fresh
bolt/projectinstall with the demo fixtures,entriesmade localized (en,nl), andproductson a custom/shop/{slugOrId}route. Bolt caches canonical links for 60 seconds incache.app, whichcache:cleardoesn't empty, so I rancache:pool:clear cache.appbefore each check. The table comparesog:urlwith Bolt's<link rel="canonical">on the same page.og:urlbeforeog:urlafter (= canonical)/page/this-is-a-page?utm_source=newsletter&page=2…?page=2&utm_source=newsletter❌/page/this-is-a-page✅/en/entry/…(default locale)/en/entry/…❌/entry/…✅/nl/entry/…/nl/entry/…✅/nl/entry/…✅/pages(listing)/pages❌/pages?order=id&status=published✅/page/does-not-exist(404)/page/does-not-exist❌/block/404-not-found✅/page/…,/,/shop/…With the change,
og:urlmatches<link rel="canonical">on all 9 URL types I checked, withremove_default_locale_on_canonicalbothtrueandfalse, and withgeneral/canonicalset to another host. Before the change, 4 of 9 differed with the setting on and 6 of 9 with it off. A canonical set in a record's SEO field still takes precedence forog:url.Tests
testCanonicalFallsBackToBoltCanonical: Bolt's canonical URL is used when nothing else sets one. It fails without the fix.testCanonicalPrefersSeoFieldOverBoltCanonical: an explicit SEO field value still wins.testCanonicalFallsBackToRequestUriWhenBoltCanonicalHasNoRoute: the request URL is used when Bolt has no canonical.The suite passes (115 tests), and PHPStan is clean.