seo: purge thin compare pages + benchmark-first title on products & compare - #908
Merged
Conversation
added 2 commits
July 5, 2026 10:49
…ompare
Bing WMT audit 2026-07-05: only 2 of 5093 sitemap URLs indexed. Root
cause: 4938 ad-hoc /compare/ pages emitted at ≥1 shared bench threshold,
producing near-duplicate templates that starved crawl budget and hurt
domain trust.
Changes:
- sitemap: hybrid threshold. Brand-whitelist pairs emit at ≥1 shared,
others at ≥3. Drops from 4938 → ~226 ad-hoc + 21 curated = ~247 URLs.
Preserves every commercial X vs Y pair users actually search for
(helius-vs-mobula, alchemy-vs-moralis, chain-vs-chain, perp-vs-perp).
- compare-pairs: remove jupiter-vs-raydium (raydium has 0 bench
appearances so /compare/jupiter-vs-raydium 404s at render).
- products/[slug]: title now leads with 'Benchmark {year}' + head-term
match on '<provider> benchmark' queries. Meta description ends with
'As of YYYY-MM-DD' for LLM citation extractability (LLM-mediated
discovery drives ~80% of Bing query traffic).
- compare/[slug]: title now leads with '{a} vs {b} Benchmark {year}'.
Meta description is unique per pair (shared bench count + date)
killing the identical duplicate signal that had Bing skipping pages.
Replaces the identical templated intro paragraph on both pages with
a data-driven prose summary derived from live measurements. Kills the
last piece of duplicate above-the-fold text so Bing sees genuinely
unique substantive content per URL.
/compare/[slug]:
Before: 'Side by side OpenChainBench measurements. Identical layout,
no editorial verdict...' (same on 247 pages)
After: 'Codex leads on 2 of 4 shared benchmarks, Mobula on 2. Codex
wins on aggregator-head-lag (128ms vs 195ms), wallet-labels-
coverage (42.8% vs 38.1%). Mobula wins on metadata-coverage
(96.9% vs 89.1%), network-coverage (80 vs 42 chains).'
/products/[slug]:
Before: 'Moralis performance benchmarks, live across 2 categories.
Reproducible measurements, open methodology.' (same on 104 pages)
After: 'Moralis is measured across 2 live OpenChainBench benchmarks,
with 1 #1 finish: NFT collection metadata (ranks #1, 96.9% p50),
Wallet labels coverage (ranks #2 of 5, 42.4% p50).'
Falls back to a neutral sentence when the provider's p50 data is
missing (cold ISR, harness restart) so cold pages don't render a lie.
Collaborator
Author
|
Follow-up commit Example /products/moralis before → after:
Example /compare/codex-vs-mobula:
Zero editorial claim, zero LLM, zero $. All prose derived deterministically from the same Prom values the table renders. Fallback to a neutral sentence when |
infrastructure/monitoring-ui and infrastructure/prom-admin are independently deployed Next.js apps (own package.json + Railway config, own build/lint/typecheck pipeline). Scanning them from the main frontend's lint pass caused every unrelated PR to fail on their pre-existing warnings that the sub-app maintainers can fix in their own dedicated PRs. worker/ is the materialize worker (own package.json + Railway config), same reasoning. Unblocks PR #908 (SEO purge) and any subsequent PR touching only src/*.
Flotapponnier
added a commit
that referenced
this pull request
Jul 5, 2026
…ompare (#908) (#909) * seo: purge thin compare pages + benchmark-first title on products & compare Bing WMT audit 2026-07-05: only 2 of 5093 sitemap URLs indexed. Root cause: 4938 ad-hoc /compare/ pages emitted at ≥1 shared bench threshold, producing near-duplicate templates that starved crawl budget and hurt domain trust. Changes: - sitemap: hybrid threshold. Brand-whitelist pairs emit at ≥1 shared, others at ≥3. Drops from 4938 → ~226 ad-hoc + 21 curated = ~247 URLs. Preserves every commercial X vs Y pair users actually search for (helius-vs-mobula, alchemy-vs-moralis, chain-vs-chain, perp-vs-perp). - compare-pairs: remove jupiter-vs-raydium (raydium has 0 bench appearances so /compare/jupiter-vs-raydium 404s at render). - products/[slug]: title now leads with 'Benchmark {year}' + head-term match on '<provider> benchmark' queries. Meta description ends with 'As of YYYY-MM-DD' for LLM citation extractability (LLM-mediated discovery drives ~80% of Bing query traffic). - compare/[slug]: title now leads with '{a} vs {b} Benchmark {year}'. Meta description is unique per pair (shared bench count + date) killing the identical duplicate signal that had Bing skipping pages. * seo: prose summary above the fold on /compare + /products Replaces the identical templated intro paragraph on both pages with a data-driven prose summary derived from live measurements. Kills the last piece of duplicate above-the-fold text so Bing sees genuinely unique substantive content per URL. /compare/[slug]: Before: 'Side by side OpenChainBench measurements. Identical layout, no editorial verdict...' (same on 247 pages) After: 'Codex leads on 2 of 4 shared benchmarks, Mobula on 2. Codex wins on aggregator-head-lag (128ms vs 195ms), wallet-labels- coverage (42.8% vs 38.1%). Mobula wins on metadata-coverage (96.9% vs 89.1%), network-coverage (80 vs 42 chains).' /products/[slug]: Before: 'Moralis performance benchmarks, live across 2 categories. Reproducible measurements, open methodology.' (same on 104 pages) After: 'Moralis is measured across 2 live OpenChainBench benchmarks, with 1 #1 finish: NFT collection metadata (ranks #1, 96.9% p50), Wallet labels coverage (ranks #2 of 5, 42.4% p50).' Falls back to a neutral sentence when the provider's p50 data is missing (cold ISR, harness restart) so cold pages don't render a lie. * chore(lint): exclude standalone sub-apps from main frontend lint infrastructure/monitoring-ui and infrastructure/prom-admin are independently deployed Next.js apps (own package.json + Railway config, own build/lint/typecheck pipeline). Scanning them from the main frontend's lint pass caused every unrelated PR to fail on their pre-existing warnings that the sub-app maintainers can fix in their own dedicated PRs. worker/ is the materialize worker (own package.json + Railway config), same reasoning. Unblocks PR #908 (SEO purge) and any subsequent PR touching only src/*. --------- Co-authored-by: Florent Tapponnier <contact@mobula.io>
Flotapponnier
added a commit
that referenced
this pull request
Jul 6, 2026
…+ rich SERP (#916) Three P0 SEO wins from post-shipping audit (2026-07-05): 1. products/[slug]/page.tsx — H1 now emits '{Provider} Benchmark' instead of bare '{Provider}'. Matches the SEO title '{Provider} Benchmark 2026' shipped in #908. Semantic mismatch was killing CTR (only 1 click observed on 'sui benchmark' at pos 2). Small styled 'Benchmark' suffix preserves visual hierarchy. 2. compare/[slug]/page.tsx — FAQPage JSON-LD. Bing + Google render rich FAQ dropdowns in SERP for pages emitting valid FAQPage. Every answer is derived from the shared benchmarks (winner + metric + link to methodology), so it re-uses data already in DOM — no editorial claim. 3. layout.tsx — RSS feed discovery + rich SERP directives: - <link rel='alternate' type='application/rss+xml' href='/rss.xml'> via Metadata.alternates.types so feed readers, Perplexity, Bing News, Claude auto-discover the existing /rss.xml route. - meta robots gains max-snippet:-1 + max-image-preview:large + max-video-preview:-1 so SERP stops truncating our data-rich prose and stops suppressing og:image on AI-scraped queries. Skipped from the audit punch list (deferred to a separate PR): - Cache-Control s-maxage / stale-while-revalidate on dynamic routes - next/image migration for WebP/AVIF - Preconnect hints for analytics.ahrefs.com The 3 shipped here are the highest-signal / lowest-risk subset. Co-authored-by: Florent Tapponnier <contact@mobula.io>
Flotapponnier
added a commit
that referenced
this pull request
Jul 6, 2026
…+ rich SERP (#916) (#917) Three P0 SEO wins from post-shipping audit (2026-07-05): 1. products/[slug]/page.tsx — H1 now emits '{Provider} Benchmark' instead of bare '{Provider}'. Matches the SEO title '{Provider} Benchmark 2026' shipped in #908. Semantic mismatch was killing CTR (only 1 click observed on 'sui benchmark' at pos 2). Small styled 'Benchmark' suffix preserves visual hierarchy. 2. compare/[slug]/page.tsx — FAQPage JSON-LD. Bing + Google render rich FAQ dropdowns in SERP for pages emitting valid FAQPage. Every answer is derived from the shared benchmarks (winner + metric + link to methodology), so it re-uses data already in DOM — no editorial claim. 3. layout.tsx — RSS feed discovery + rich SERP directives: - <link rel='alternate' type='application/rss+xml' href='/rss.xml'> via Metadata.alternates.types so feed readers, Perplexity, Bing News, Claude auto-discover the existing /rss.xml route. - meta robots gains max-snippet:-1 + max-image-preview:large + max-video-preview:-1 so SERP stops truncating our data-rich prose and stops suppressing og:image on AI-scraped queries. Skipped from the audit punch list (deferred to a separate PR): - Cache-Control s-maxage / stale-while-revalidate on dynamic routes - next/image migration for WebP/AVIF - Preconnect hints for analytics.ahrefs.com The 3 shipped here are the highest-signal / lowest-risk subset. Co-authored-by: Florent Tapponnier <contact@mobula.io>
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.
Summary
Bing WMT audit 2026-07-05 showed only 2 of 5093 sitemap URLs indexed. Root cause: 4938 ad-hoc
/compare/*pages emitted at ≥1 shared bench threshold, producing near-duplicate templates that starved Bing's crawl budget and hurt domain trust.Changes
1. Sitemap — hybrid threshold (
src/app/sitemap.ts+ newsrc/lib/compare/brand-whitelist.ts)BRAND_WHITELIST→ emit at ≥ 1 shared bench2. Curated pair fix (
src/data/compare-pairs.ts)jupiter-vs-raydium— raydium has 0 bench appearances so the page 404s at render viahasSharedBenches(). Re-add when raydium is measured in any OCB bench.3. Product page SEO (
src/app/products/[slug]/page.tsx){Provider} Benchmark {year}— head-term match on<provider> benchmarkqueries (the query pattern OCB gets from LLM search).As of YYYY-MM-DD— LLM extractability signal (ChatGPT/Perplexity/Copilot cite dated content more).4. Compare page SEO (
src/app/compare/[slug]/page.tsx){a} vs {b} Benchmark {year}.Numbers
Simulated locally on current bench specs:
Test plan
pnpm typecheck— cleanpnpm lint— cleanFollow-ups (separate PRs)
<lastmod>from actual data timestamps (currently a single global timestamp)