StatsHub Docs
Audits

StatsHub SEO Audit Report

A full technical SEO review of the web app as it stood on 2026-01-29 — per-page grades, missing schema, and the fixes proposed at the time.

Date: January 29, 2026 Framework: Next.js 14.2.18 (Pages Router) Pages Audited: 32 (30 routes + 2 global files)


Executive Summary

Overall Health: ⚠️ Mixed

StatsHub has strong SEO foundations on key pages (homepage, blog, betting-academy, referees, stats-explained, odds-converter) but significant gaps on tool pages and the entire basketball section.

Top 5 Priority Issues

  1. Basketball pages have zero SEO — All 3 basketball dynamic routes (fixture, player, team) use generic football-focused MetaTags with no dynamic data, no structured data, and hardcoded og
    to the homepage. Grade: F
  2. Tool pages missing all SEO tags/prop-screener, /team-screener, and /value-bets have no title, meta description, canonical URL, OG tags, or structured data. Grade: F
  3. MetaTags component og
    bug
    — The default MetaTags component hardcodes og:url to https://statshub.com/ for all pages, causing incorrect OG URLs across 8+ pages.
  4. Client-side rendering on indexable pages — Most static content pages (features, free-tools, odds-converter, player-stats, team-stats) use CSR instead of SSG, meaning crawlers may not see content.
  5. League/Team dynamic meta tags invisible to crawlers — Rich meta tag data (season, team count, champion) depends on client-side SWR fetches, so SSR HTML lacks this information.

Quick Wins

  • Add <Head> blocks with title/description to /prop-screener, /team-screener, /value-bets (~30 min)
  • Fix /lineup-bot canonical from statshub.io to statshub.com (~1 min)
  • Add <meta name="robots" content="noindex" /> to /404 and /favourites (~5 min)
  • Fix &apos; HTML entities in meta descriptions on /free-tools and /odds-converter (~5 min)
  • Add getStaticProps to purely static pages like /features, /free-tools, /odds-converter (~15 min each)

Global Files Audit

_document.tsx

File: src/pages/_document.tsx

CheckStatusDetails
HTML lang<Html lang="en"> properly set
ViewportConfigured with viewport-fit=cover for notch support
Favicons32x32, 16x16, apple-touch-icon all present
PWA Manifest/manifest.json linked
PWA Meta Tagsapplication-name, theme-color, apple-mobile-web-app-capable
AnalyticsGoogle Analytics (G-FNRHWN8908) + Umami analytics
Default TitleNo default <title> fallback — pages without Head will show no title

Issues:

  • Viewport meta tag is in _document.tsx Head, but Next.js recommends putting viewport in _app.tsx or per-page. Having it here works but triggers a Next.js warning.
  • No default <title> tag — pages that forget to add their own will have no title.

Recommendations:

  • Add a default <title>StatsHub | Free Football & Basketball Stats</title> as a fallback.
  • Consider moving viewport to next.config.js viewport export.

_app.tsx

File: src/pages/_app.tsx

CheckStatusDetails
Error BoundaryWraps entire app
Theme Supportnext-themes with system default
Layout SystemPer-page layouts via getLayout
SWR ConfigDeduplication and revalidation configured
Global SEONo global meta tags, no DefaultSeo component

Issues:

  • No global SEO defaults (e.g., next-seo's DefaultSeo) — each page must independently define all meta tags.

Recommendations:

  • Consider adding a global SEO defaults component that provides fallback title, description, OG tags for any page that doesn't override them.

Static Pages

/

File: src/pages/index.tsx | Rendering: CSR

CheckStatusDetails
Title Tag"Free Football & Player Stats -- Shots, Tackles + More | StatsHub" (64 chars — slightly over 60)
Meta DescriptionPresent, ~170 chars. Slightly over 160 but keyword-rich
Open Graph TagsFull suite: og
, og
, og
, og
, og
, og
, og
, og
dimensions
Twitter Card TagsFull suite: twitter
, twitter
, twitter
, twitter
, twitter
, twitter
Canonical URLSelf-referencing: https://statshub.com
H1 Tag⚠️"Today's Football Fixtures, Player Stats & Team Stats" — keyword-rich, but two H1 tags on page
Heading Hierarchy⚠️Two H1s detected. H2/H3 follow logically after the first H1
Structured DataExcellent — WebSite, FAQPage, Organization, and dynamic SportsEvent ItemList schemas
Image Alt TextTournament logos, team logos all have alt attributes
Rendering⚠️CSR only — fixture data fetched client-side, not visible to crawlers

Issues:

  • Two H1 tags on the page
  • Title 64 chars (slightly over 60)
  • Meta description ~170 chars (over 160)
  • CSR — fixture data invisible to crawlers

Recommendations:

  • Reduce to single H1
  • Trim title to under 60 chars
  • Trim description to 150-160 chars
  • Add getServerSideProps or getStaticProps with ISR for fixture data

/100-club

File: src/pages/100-club.tsx | Rendering: CSR

CheckStatusDetails
Title Tag⚠️Generic default from MetaTags — not specific to 100% Club
Meta Description⚠️Generic default — not page-specific
Open Graph Tags⚠️Generic, og
points to homepage
Twitter Card Tags⚠️Generic defaults
Canonical URLNot set
H1 Tag"100% CLUB"
Heading Hierarchy⚠️H1 followed by H4, H3, H2 — skips levels
Structured DataNone
Image Alt TextAvatarImage has alt attributes
RenderingCSR only — no content for crawlers

Issues:

  • All meta tags are generic defaults
  • No canonical URL, no structured data
  • Heading hierarchy broken (H1 → H4)
  • CSR with no pre-rendered content

Recommendations:

  • Add custom title: "100% Club - Perfect Hit Rate Player Props | StatsHub" (~55 chars)
  • Add page-specific meta description
  • Add canonical: https://statshub.com/100-club
  • Fix heading hierarchy: H1 → H2 → H3
  • Add WebPage JSON-LD

/favourites

File: src/pages/favourites.tsx | Rendering: SSR (empty props)

CheckStatusDetails
Title Tag⚠️Generic default
Meta Description⚠️Generic default
Open Graph Tags⚠️Two meta tag components (MetaTags + EnhancedMetaTags) may conflict
Twitter Card Tags⚠️Generic defaults
Canonical URLNot set
H1 Tag"MY FAVORITES" / "Sign in to view favorites"
Heading Hierarchy⚠️Acceptable but user-specific, not SEO-oriented
Structured DataNone
Image Alt TextAlt attributes present
Rendering⚠️SSR returns empty props — effectively CSR

Issues:

  • Auth-gated page with generic meta tags
  • Two meta tag components may cause duplicate tags
  • getServerSideProps returns empty props

Recommendations:

  • Add <meta name="robots" content="noindex, nofollow" /> — this is a user-private page
  • Remove duplicate MetaTags + EnhancedMetaTags — use one approach

/features

File: src/pages/features.tsx | Rendering: CSR

CheckStatusDetails
Title Tag⚠️"Football Stats Tools & player stats features | StatsHub PropHunter + More" (74 chars — too long)
Meta DescriptionPresent, ~155 chars, keyword-rich
Open Graph Tags⚠️Missing og
and og
Twitter Card Tags⚠️Missing twitter
and twitter
Canonical URLNot set
H1 Tag"Powerful Football Analytics Features"
Heading HierarchyGood: H1 → H2 → H3
Structured DataWebPage + SoftwareApplication with featureList
Image Alt Text⚠️N/A — uses Lucide icons
Rendering⚠️CSR — entirely static content, should be SSG

Issues:

  • Title too long (74 chars)
  • Missing og
    , og
    , twitter
    , twitter
  • No canonical URL
  • Static content rendered client-side

Recommendations:

  • Shorten title to ~55 chars: "Football Analytics Features & Tools | StatsHub"
  • Add missing OG/Twitter tags
  • Add canonical: https://statshub.com/features
  • Add getStaticProps for SSG

/free-tools

File: src/pages/free-tools.tsx | Rendering: CSR

CheckStatusDetails
Title Tag"Free Football Betting Tools & Analytics | StatsHub" (51 chars)
Meta Description⚠️Contains &apos; HTML entity that renders literally
Open Graph TagsFull suite present
Twitter Card Tags⚠️Missing twitter
Canonical URLSelf-referencing: https://statshub.com/free-tools
H1 Tag"Free Football Betting Tools"
Heading HierarchyGood: H1 → H2 → H3 → H4
Structured DataWebPage + BreadcrumbList
Image Alt Text⚠️N/A — Lucide icons
Rendering⚠️CSR — static content ideal for SSG

Issues:

  • &apos; in meta description won't render as apostrophe
  • Missing twitter
  • Static content should use SSG

Recommendations:

  • Fix &apos;' in meta description
  • Add twitter
  • Add getStaticProps for SSG

/lineup-bot

File: src/pages/lineup-bot.tsx | Rendering: SSG

CheckStatusDetails
Title Tag"Lineup Hunter AI - Premium Betting Intelligence | Statshub" (60 chars)
Meta Description⚠️Present but only 112 chars — under 150 target
Open Graph Tags⚠️Missing og
, og
, og
Twitter Card TagsNo Twitter Card tags at all
Canonical URL⚠️Points to statshub.io instead of statshub.comwrong domain
H1 Tag"Detect Lineup Changes. Exploit Mispriced Odds."
Heading HierarchyGood: H1 → H2 → H3
Structured DataNone
Image Alt Text⚠️No images on page
RenderingSSG via getStaticProps

Issues:

  • Canonical URL uses wrong domain (statshub.io vs statshub.com)
  • No Twitter Card tags
  • Missing og
    , og
    , og
  • No structured data
  • Brand inconsistency: "Statshub" vs "StatsHub"

Recommendations:

  • Fix canonical to https://statshub.com/lineup-bot
  • Add Twitter Card tags
  • Add missing OG tags
  • Add Product or Service structured data
  • Expand meta description to 150-160 chars
  • Standardize brand to "StatsHub"

/odds-converter

File: src/pages/odds-converter.tsx | Rendering: CSR

CheckStatusDetails
Title Tag⚠️"Odds Converter | Convert Fractional, Decimal & American Odds Instantly" (72 chars — over 60)
Meta Description~155 chars, keyword-rich
Open Graph TagsFull suite present
Twitter Card Tags⚠️Missing twitter
Canonical URLSelf-referencing: https://statshub.com/odds-converter
H1 Tag"Odds Converter"
Heading HierarchyExcellent: H1 → H2 → H3
Structured DataExcellent — WebPage, FAQPage, BreadcrumbList, VideoObject
Image Alt Text⚠️YouTube iframe has title but no img tags
Rendering⚠️CSR — static content ideal for SSG

Issues:

  • Title 72 chars — will be truncated
  • Missing twitter
  • &apos; HTML entity in FAQ JSON-LD
  • Static content should use SSG

Recommendations:

  • Shorten title to ~60 chars
  • Add twitter
  • Fix &apos; in FAQ data
  • Add getStaticProps — best candidate for SSG template

/outliers

File: src/pages/outliers.tsx | Rendering: CSR

CheckStatusDetails
Title Tag⚠️Generic default — not specific to outliers
Meta Description⚠️Generic default
Open Graph Tags⚠️Generic, og
points to homepage
Twitter Card Tags⚠️Generic defaults
Canonical URLNot set
H1 Tag"Betting Market Outliers"
Heading Hierarchy⚠️Only H1 — no H2 or H3 subheadings
Structured DataNone
Image Alt Text⚠️No images
RenderingCSR only — crawlers see empty page

Issues:

  • All meta tags generic, no canonical, no structured data
  • No heading hierarchy below H1
  • CSR with no content for crawlers

Recommendations:

  • Add custom title: "Betting Market Outliers - Odds Discrepancy Finder | StatsHub"
  • Add page-specific meta description
  • Add canonical: https://statshub.com/outliers
  • Add WebPage JSON-LD
  • Add H2 section headings

/player-stats

File: src/pages/player-stats.tsx | Rendering: CSR

CheckStatusDetails
Title Tag⚠️"Football Player Stats -- xG, Shots, Tackles, Shots On Target & More | StatsHub" (74 chars — too long)
Meta Description156 chars, compelling and keyword-rich
Open Graph TagsFull suite present
Twitter Card TagsFull suite present
Canonical URLSelf-referencing: https://statshub.com/player-stats
H1 Tag"Explore Player Stats in One Place"
Heading HierarchyLogical: H1 → H2 → H3
Structured DataWebPage + BreadcrumbList + FAQPage (5 questions)
Image Alt Text⚠️No images — emoji icons used
Rendering⚠️CSR — static content should use SSG

Issues:

  • Title 74 chars — will be truncated
  • Duplicate MetaTags component alongside custom Head tags
  • Static content using CSR

Recommendations:

  • Shorten title to ~58 chars
  • Remove MetaTags component (custom Head already defines everything)
  • Add getStaticProps for SSG

/prop-screener

File: src/pages/prop-screener.tsx | Rendering: CSR

CheckStatusDetails
Title TagNo title — only generic MetaTags default
Meta DescriptionNo page-specific description
Open Graph TagsGeneric defaults, og
points to homepage
Twitter Card TagsGeneric defaults
Canonical URLNot set
H1 Tag"PROP SCREENER"
Heading Hierarchy⚠️H1 present, no H2s, H3 used for empty states
Structured DataNone
Image Alt Text⚠️No images
Rendering⚠️CSR — acceptable for dynamic tool but shell could be SSR

Issues:

  • Missing all page-specific SEO: title, description, canonical, OG, Twitter, JSON-LD

Recommendations:

  • Add title: "Prop Screener - Filter Player Props by Hit Rate & Odds | StatsHub"
  • Add meta description targeting "prop screener", "player props"
  • Add canonical: https://statshub.com/prop-screener
  • Add WebApplication JSON-LD

/prophunter

File: src/pages/prophunter.tsx | Rendering: SSR

CheckStatusDetails
Title Tag⚠️"PropHunter -- Find Value in Player Props Using Football player Stats | StatsHub" (82 chars — too long)
Meta Description⚠️176 chars — over 160 target
Open Graph TagsFull suite present
Twitter Card TagsFull suite present
Canonical URLSelf-referencing: https://statshub.com/prophunter
H1 Tag"PROP HUNTER"
Heading Hierarchy⚠️H1 only — no H2/H3 in page content
Structured DataNone
Image Alt Text⚠️No standard images visible
RenderingSSR via GetServerSideProps

Issues:

  • Title 82 chars — too long
  • Meta description 176 chars — too long
  • "Football player Stats" should be "Football Player Stats" (capitalization)
  • No structured data

Recommendations:

  • Shorten title to ~60 chars
  • Trim description to 155-160 chars
  • Fix capitalization
  • Add WebApplication JSON-LD

/referees

File: src/pages/referees.tsx | Rendering: CSR

CheckStatusDetails
Title Tag"Football Referee Stats & Card Statistics | StatsHub" (51 chars)
Meta Description161 chars — just 1 char over ideal
Open Graph TagsFull suite present
Twitter Card TagsFull suite present
Canonical URLSelf-referencing: https://statshub.com/referees
H1 Tag"Referee Stats"
Heading HierarchyH1 → H2 (FAQ) with accordion items
Structured DataFAQPage with 8 questions
Image Alt Textalt={referee.name} on all avatars
Rendering⚠️CSR — data fetched via SWR

Issues:

  • Description 1 char over 160 limit
  • CSR means referee data table not visible to crawlers on first load

Recommendations:

  • Trim description by 1-2 chars
  • Consider SSR for initial data table rendering
  • Add WebPage JSON-LD alongside FAQPage

/stats-explained

File: src/pages/stats-explained.tsx | Rendering: SSG (ISR, daily)

CheckStatusDetails
Title Tag⚠️"Football Stats Explained | Complete 2025 Guide to Player Stats & Football Statistics - StatsHub" (94 chars — very long)
Meta Description145 chars, keyword-rich
Open Graph TagsFull suite with image dimensions and alt text
Twitter Card TagsFull suite with image alt
Canonical URLSelf-referencing: https://statshub.com/stats-explained
H1 Tag"Football Stats Explained"
Heading HierarchyExcellent: H1 → H2 → H3 → H4
Structured DataArticle + FAQPage — both well-structured
Image Alt Text⚠️No images — emoji icons
RenderingSSG with ISR (revalidate: 86400)

Issues:

  • Title 94 chars — far too long
  • Contains "2025" which is now outdated (January 2026)

Recommendations:

  • Shorten title to ~60 chars: "Football Stats Explained - xG, Passes & More | StatsHub"
  • Update or remove the year reference
  • Best-optimized page — use as template for others

/team-screener

File: src/pages/team-screener.tsx | Rendering: CSR

CheckStatusDetails
Title TagNo title — generic MetaTags default
Meta DescriptionNo page-specific description
Open Graph TagsGeneric defaults
Twitter Card TagsGeneric defaults
Canonical URLNot set
H1 Tag"TEAM SCREENER"
Heading Hierarchy⚠️H1 → H3 (no H2)
Structured DataNone
Image Alt Text⚠️No images
Rendering⚠️CSR

Issues:

  • Missing all page-specific SEO tags

Recommendations:

  • Add title: "Team Screener - Filter Team Props by Odds & Hit Rates | StatsHub"
  • Add meta description, canonical, OG tags, Twitter cards, JSON-LD

/team-stats

File: src/pages/team-stats.tsx | Rendering: CSR

CheckStatusDetails
Title Tag⚠️"Football Stats | Soccer Statistics | Form Tables | StatsHub" (58 chars) — doesn't mention "Team Stats"
Meta Description⚠️133 chars — under 150 target
Open Graph TagsFull suite present
Twitter Card TagsFull suite present
Canonical URLSelf-referencing: https://statshub.com/team-stats
H1 Tag"Comprehensive Team Stats"
Heading HierarchyExcellent: H1 → H2 → H3
Structured DataWebPage + BreadcrumbList + FAQPage
Image Alt Text⚠️No images — emoji icons
Rendering⚠️CSR — static content should be SSG

Issues:

  • Title doesn't mention "Team Stats" which is the page focus
  • Description too short (133 chars)
  • Duplicate MetaTags component
  • Static content using CSR

Recommendations:

  • Update title: "Football Team Stats - Goals, xG, Shots & More | StatsHub"
  • Expand description to 150-160 chars
  • Remove MetaTags component
  • Add getStaticProps for SSG

/value-bets

File: src/pages/value-bets.tsx | Rendering: CSR

CheckStatusDetails
Title TagNo title — generic MetaTags default
Meta DescriptionNo page-specific description
Open Graph TagsGeneric defaults
Twitter Card TagsGeneric defaults
Canonical URLNot set
H1 Tag"Value Bets"
Heading Hierarchy⚠️H1 only — no semantic sub-sections
Structured DataNone
Image Alt Textalt={source} and alt={bet.source} present
Rendering⚠️CSR

Issues:

  • Missing all page-specific SEO tags

Recommendations:

  • Add title: "Value Bets - Football Betting Edge Finder | StatsHub"
  • Add meta description, canonical, OG tags, Twitter cards, JSON-LD

/404

File: src/pages/404.tsx | Rendering: SSG (Next.js default)

CheckStatusDetails
Title TagNo Head component at all
Meta DescriptionNone
Open Graph TagsNone
Twitter Card TagsNone
Canonical URLN/A for 404
H1 Tag"404 - Page Not Found"
Heading HierarchySingle H1 — appropriate for 404
Structured DataN/A for 404
Image Alt Text⚠️No images
RenderingSSG (default for 404)

Issues:

  • No <Head> — browser tab shows default/empty title
  • No <meta name="robots" content="noindex"> to prevent indexing

Recommendations:

  • Add <Head> with title: "404 - Page Not Found | StatsHub"
  • Add <meta name="robots" content="noindex, nofollow" />

Index / Landing Pages

/blog

File: src/pages/blog/index.tsx | Rendering: SSG (ISR, revalidate: 60s)

CheckStatusDetails
Title Tag⚠️"Football Blog | Expert Analysis, Player Stats & Football Insights - StatsHub" (74 chars — over 60)
Meta Description⚠️183 chars — over 160 target
Open Graph TagsFull suite with image dimensions and alt text
Twitter Card TagsFull suite with image alt
Canonical URLSelf-referencing: https://statshub.com/blog
H1 Tag"Football Blog & Expert Analysis"
Heading HierarchyH1 → H2 → H3
Structured DataBlog with BlogPosting items + FAQPage
Image Alt Text⚠️Depends on CardArticleUnified component
RenderingSSG with ISR

Issues:

  • Title 74 chars — will be truncated
  • Description 183 chars — over limit
  • Generic og

Recommendations:

  • Shorten title to ~55 chars
  • Trim description to 155 chars
  • Create unique blog-specific og

/betting-academy

File: src/pages/betting-academy/index.tsx | Rendering: SSG (ISR, revalidate: 60s)

CheckStatusDetails
Title Tag⚠️"Betting Academy | Expert Betting Guides & Strategies - StatsHub" (65 chars — slightly over)
Meta Description⚠️169 chars — slightly over 160 target
Open Graph TagsFull suite with dimensions
Twitter Card TagsFull suite with alt
Canonical URLSelf-referencing: https://statshub.com/betting-academy
H1 Tag"StatsHub Betting Academy"
Heading HierarchyH1 → H2 → H3
Structured DataEducationalOrganization + FAQPage
Image Alt Text⚠️Depends on CardArticleUnified component
RenderingSSG with ISR

Issues:

  • Title 65 chars — slightly over
  • Description 169 chars — slightly over
  • EducationalOrganization schema uses courseMode/teaches which belong to Course schema

Recommendations:

  • Trim title to ~55 chars
  • Trim description to 155 chars
  • Fix JSON-LD type or remove non-standard properties
  • Create unique og

/league

File: src/pages/league/index.tsx | Rendering: CSR

CheckStatusDetails
Title TagNo Head component at all
Meta DescriptionNone
Open Graph TagsNone
Twitter Card TagsNone
Canonical URLNone
H1 Tag"Football Leagues"
Heading HierarchyH1 → H2 ("Popular Leagues", "All Leagues") → H3
Structured DataNone
Image Alt Textalt={league.name} present
RenderingCSR via useSWR — crawlers see empty shell

Issues:

  • Most SEO-deficient page in the audit — no Head, no meta tags, no structured data, CSR only
  • League list invisible to crawlers

Recommendations:

  • Add full <Head> block with title, description, OG tags, Twitter cards
  • Add canonical: https://statshub.com/league
  • Add getStaticProps or getServerSideProps to pre-render league list
  • Add ItemList or CollectionPage JSON-LD
  • Critical fix — this is a high-value SEO page

/basketball

File: src/pages/basketball/index.tsx | Rendering: CSR

CheckStatusDetails
Title Tag"Basketball Games & Scores | StatsHub" (43 chars)
Meta Description⚠️119 chars — under 150 target
Open Graph Tags⚠️Missing og
, og
, og
Twitter Card TagsNone present
Canonical URLhttps://statshub.com/basketball
H1 Tag"Today's Basketball Games & Scores"
Heading HierarchyH1 → H2
Structured DataNone
Image Alt TextAlt attributes on team/tournament images
RenderingCSR via useSWR — game data invisible to crawlers

Issues:

  • Short meta description (119 chars)
  • Missing OG tags (og
    , og
    , og
    )
  • No Twitter Card tags
  • No structured data
  • CSR only

Recommendations:

  • Expand description to 150-160 chars
  • Add missing OG and Twitter Card tags
  • Add SportsEvent or WebPage JSON-LD
  • Consider SSR for game data

Dynamic Route Templates

/betting-academy/[slug]

File: src/pages/betting-academy/[slug].tsx | Rendering: SSG (ISR, revalidate: 60s)

CheckStatusDetails
Title TagDynamic: {post.title} - StatsHub Betting Academy
Meta DescriptionDynamic: uses post.metaDescription from CMS
Open Graph TagsAll present with dynamic data, conditional cover image
Twitter Card Tagssummary_large_image with dynamic data
Canonical URLDynamic: https://statshub.com/betting-academy/${post.slug}
H1 TagDynamic from post.title via BlogHero
Heading HierarchyH1 in hero, H2/H3 via markdown renderer
Structured DataArticle schema with dynamic headline, dates, author, publisher
Image Alt Text⚠️Depends on BlogHero component
RenderingSSG with ISR
Dynamic SEOAll meta tags fully data-driven

Issues: None significant. Best-optimized page in the codebase.

Recommendations:

  • Verify BlogHero applies alt text to cover image
  • Consider adding twitter:site handle

/fixture/[fixtureSlug]/[fixtureId]

File: src/pages/fixture/[fixtureSlug]/[fixtureId].tsx | Rendering: SSR

CheckStatusDetails
Title TagDynamic: {team1} vs {team2} {date} | Live Stats, H2H, Lineups & Analysis
Meta DescriptionDynamic with both team names and date
Open Graph TagsFull suite via EnhancedMetaTags
Twitter Card Tagssummary_large_image with dynamic data
Canonical URLDynamic: https://statshub.com/fixture/{slug}/{id}
H1 Tag⚠️No explicit H1 — relies on FixtureHeader component
Heading Hierarchy⚠️No visible headings in page file
Structured DataSportsEvent with dynamic teams, date, venue, league
Image Alt TextDynamic alt text on team avatars
RenderingSSR
Dynamic SEOAll meta tags use dynamic fixture data

Issues:

  • og
    defaults to generic og-image.png
  • No explicit H1 visible in page file

Recommendations:

  • Verify FixtureHeader renders an H1
  • Consider dynamic OG image generation per fixture

/player/[playerSlug]/[playerId]

File: src/pages/player/[playerSlug]/[playerId].tsx | Rendering: SSR

CheckStatusDetails
Title TagDynamic: {playerName} Stats | {teamName} {position} Performance & Analysis
Meta DescriptionDynamic with player and team names
Open Graph TagsFull suite via EnhancedMetaTags
Twitter Card Tagssummary_large_image with dynamic data
Canonical URLDynamic: https://statshub.com/player/{slug}/{id}
H1 Tag⚠️No explicit H1 — relies on PlayerHeader
Heading Hierarchy⚠️Depends on child components
Structured DataPerson schema with name, team, position, nationality, age, jersey
Image Alt TextAlt attributes on player/team avatars
RenderingSSR
Dynamic SEO⚠️Stats not passed to EnhancedMetaTags — description uses generic fallback

Issues:

  • Player statistics not passed to meta tags — description says "detailed stats" instead of actual numbers
  • og
    defaults to generic
  • No explicit H1

Recommendations:

  • Pass stats prop to EnhancedMetaTags from getServerSideProps
  • Verify PlayerHeader renders H1
  • Consider player-specific OG image

/team/[teamSlug]/[teamId]

File: src/pages/team/[teamSlug]/[teamId].tsx | Rendering: SSR

CheckStatusDetails
Title TagDynamic: {teamName} Stats {season} | Squad, Fixtures, Results & Analysis
Meta DescriptionDynamic with team name
Open Graph TagsFull suite via EnhancedMetaTags
Twitter Card Tagssummary_large_image with dynamic data
Canonical URLDynamic: https://statshub.com/team/{slug}/{id}
H1 Tag⚠️No explicit H1 — relies on TeamHeader
Heading Hierarchy⚠️Depends on child components
Structured DataSportsTeam with name, league, venue
Image Alt TextAlt attributes present
RenderingSSR
Dynamic SEO⚠️Season prop depends on client-side SWR — undefined at SSR time

Issues:

  • season depends on client-side data — crawlers see no season in title
  • og
    defaults to generic

Recommendations:

  • Fetch season in getServerSideProps
  • Verify TeamHeader renders H1
  • Consider team badge as OG image

/league/[leagueSlug]/[leagueId]

File: src/pages/league/[leagueSlug]/[leagueId].tsx | Rendering: SSR

CheckStatusDetails
Title TagDynamic: {leagueName} {season} Table, Fixtures & Results {country} | {teamCount} Teams | StatsHub
Meta DescriptionHighly dynamic: league name, season, team count, champion, match count
Open Graph TagsFull suite via EnhancedMetaTags
Twitter Card TagsEnhanced with twitter
/data1 for top scorer
Canonical URLDynamic: https://statshub.com/league/{slug}/{id}
H1 Tag⚠️No explicit H1 — relies on LeagueHeader
Heading Hierarchy⚠️Depends on child components
Structured DataExcellent: SportsOrganization, SportsCompetition, BreadcrumbList, FAQPage
Image Alt Text⚠️In child components
RenderingSSR
Dynamic SEO⚠️Critical: season, teamCount, champion, leagueStats all from client-side SWR — absent in SSR HTML

Issues:

  • High impact: All rich meta tag data is invisible to crawlers because it depends on client-side SWR
  • lastUpdated runs on client, not server
  • og
    defaults to generic

Recommendations:

  • Fetch standings data in getServerSideProps so teamCount, champion, season are in SSR HTML
  • This is the highest-impact fix — excellent meta logic but invisible to crawlers
  • Verify LeagueHeader renders H1

/referee/[refereeSlug]

File: src/pages/referee/[refereeSlug].tsx | Rendering: SSR

CheckStatusDetails
Title TagDynamic: {refereeName} Referee Stats | {yellowCards/game} | Match History & Analysis
Meta DescriptionDynamic: referee name, games officiated, cards per game
Open Graph TagsFull suite with referee-specific image
Twitter Card Tagssummary_large_image with dynamic data
Canonical URLDynamic: https://statshub.com/referee/{slug}
H1 Tag⚠️No explicit H1 — relies on RefereeHeader
Heading HierarchyH2 → H3 → H4 in SEO content section
Structured DataPerson, BreadcrumbList, FAQPage — all fully dynamic
Image Alt TextAlt attributes with tournament names
RenderingSSR with all key data prefetched
Dynamic SEOAll meta data from SSR props — most complete dynamic page

Issues:

  • No explicit H1 visible (relies on RefereeHeader)
  • Referee image may 404 if not uploaded

Recommendations:

  • Verify RefereeHeader renders H1
  • Add fallback for og

/basketball/fixture/[fixtureSlug]/[fixtureId]

File: src/pages/basketball/fixture/[fixtureSlug]/[fixtureId].tsx | Rendering: SSR

CheckStatusDetails
Title TagGeneric "Free Football & Player Stats" — not basketball, not dynamic
Meta DescriptionGeneric football description
Open Graph TagsGeneric, og
hardcoded to homepage
Twitter Card TagsGeneric defaults
Canonical URLNot set
H1 Tag⚠️Relies on BasketballFixtureHeader
Heading Hierarchy⚠️No headings in page file
Structured DataNone
Image Alt TextTeam alt text present
RenderingSSR
Dynamic SEONo dynamic SEO whatsoever

Issues:

  • Zero SEO — all meta tags are generic football defaults on a basketball page

Recommendations:

  • Replace MetaTags with EnhancedMetaTags using basketball fixture data
  • Add StructuredData with sportsEvent type
  • Add canonical URL

/basketball/player/[playerSlug]/[playerId]

File: src/pages/basketball/player/[playerSlug]/[playerId].tsx | Rendering: SSR

CheckStatusDetails
Title Tag⚠️Dynamic but generic: {playerName} - Player Stats
Meta Description⚠️References "goals, assists, shots" — football terminology on basketball page
Open Graph Tags⚠️Dynamic title but og
hardcoded to homepage
Twitter Card Tags⚠️Dynamic title but generic image
Canonical URLNot set
H1 Tag⚠️Relies on BasketballPlayerHeader
Heading Hierarchy⚠️No headings in page file
Structured DataNone
Image Alt TextAlt text present
RenderingSSR with slug redirect
Dynamic SEO⚠️Partially dynamic but uses wrong sport terminology

Issues:

  • Meta description uses football terms ("goals, assists, shots") instead of basketball ("points, rebounds, assists")
  • No canonical, no structured data, og
    wrong

Recommendations:

  • Create basketball-specific meta component with basketball terminology
  • Add canonical: https://statshub.com/basketball/player/{slug}/{id}
  • Add Person structured data
  • Fix og

/basketball/team/[teamSlug]/[teamId]

File: src/pages/basketball/team/[teamSlug]/[teamId].tsx | Rendering: SSR

CheckStatusDetails
Title TagGeneric "Free Football & Player Stats" — not basketball, not dynamic
Meta DescriptionGeneric football description
Open Graph TagsGeneric, og
hardcoded to homepage
Twitter Card TagsGeneric defaults
Canonical URLNot set
H1 Tag⚠️Relies on BasketballTeamHeader
Heading Hierarchy⚠️No headings in page file
Structured DataNone
Image Alt TextTeam alt text present
RenderingSSR
Dynamic SEONo dynamic SEO whatsoever

Issues:

  • Zero SEO — generic football defaults on a basketball page

Recommendations:

  • Replace MetaTags with EnhancedMetaTags using basketball team data
  • Add SportsTeam structured data
  • Add canonical: https://statshub.com/basketball/team/{slug}/{id}

Overall Scorecard

PageTitleDescOGTwitterCanonicalH1JSON-LDRenderingGrade
/⚠️⚠️B+
/100-club⚠️⚠️⚠️⚠️D
/favourites⚠️⚠️⚠️⚠️⚠️D
/features⚠️⚠️⚠️⚠️C+
/free-tools⚠️⚠️⚠️B
/lineup-bot⚠️⚠️⚠️C
/odds-converter⚠️⚠️⚠️B+
/outliers⚠️⚠️⚠️⚠️D
/player-stats⚠️⚠️B+
/prop-screener⚠️F
/prophunter⚠️⚠️B-
/referees⚠️A-
/stats-explained⚠️A-
/team-screener⚠️F
/team-stats⚠️⚠️⚠️B
/value-bets⚠️F
/404D
/blog⚠️⚠️A-
/betting-academy⚠️⚠️A-
/leagueF
/basketball⚠️⚠️C-
/betting-academy/[slug]A
/fixture/[slug]/[id]⚠️A-
/player/[slug]/[id]⚠️B+
/team/[slug]/[id]⚠️B+
/league/[slug]/[id]⚠️B
/referee/[slug]⚠️A
/basketball/fixture⚠️F
/basketball/player⚠️⚠️⚠️⚠️⚠️D
/basketball/team⚠️F

Prioritized Action Plan

Critical Fixes (Blocking Ranking)

#FixPages AffectedImpact
1Add page-specific <Head> with title, description, canonical, OG, Twitter to F-graded pages/prop-screener, /team-screener, /value-bets, /leagueHigh — these pages are invisible to search engines
2Replace generic football MetaTags with basketball-specific EnhancedMetaTags on all basketball dynamic routes/basketball/fixture, /basketball/player, /basketball/teamHigh — wrong sport terminology, no dynamic data
3Fix MetaTags component og:url bug — hardcoded to homepage for all "default" pages8+ pages using MetaTags pageType="default"High — incorrect OG URL across the site
4Fix /lineup-bot canonical URL from statshub.io to statshub.com/lineup-botHigh — pointing crawlers to wrong domain

High-Impact Improvements

#FixPages AffectedImpact
5Move league standings/season data fetch into getServerSideProps/league/[slug]/[id]High — rich meta tags invisible to crawlers
6Pass player stats to EnhancedMetaTags from getServerSideProps/player/[slug]/[id]Medium — generic description instead of actual stats
7Fetch season in getServerSideProps for team pages/team/[slug]/[id]Medium — season missing from title at SSR
8Audit all Header components for H1 renderingAll dynamic route pagesMedium — may be missing H1 tags

Quick Wins (Easy, Immediate Benefit)

#FixPages AffectedImpact
9Add <meta name="robots" content="noindex" /> to non-indexable pages/404, /favouritesLow — prevents wasted crawl budget
10Fix &apos; HTML entities in meta descriptions and JSON-LD/free-tools, /odds-converterLow — broken characters in SERPs
11Shorten over-length title tags to 50-60 chars/, /blog, /betting-academy, /features, /player-stats, /prophunter, /stats-explained, /odds-converterMedium — prevents SERP truncation
12Add missing twitter:image tags/features, /free-tools, /odds-converter, /lineup-botLow — missing social sharing image
13Update "2025" to "2026" in /stats-explained title/stats-explainedLow — outdated year reference

Long-Term Recommendations

#FixPages AffectedImpact
14Add getStaticProps to all purely static content pages for SSG/features, /free-tools, /odds-converter, /player-stats, /team-statsMedium — faster loads, better crawlability
15Create a global DefaultSeo component with fallback meta tagsAll pagesMedium — prevents pages from having zero SEO
16Migrate all remaining MetaTags usage to EnhancedMetaTagsAll pages still using MetaTagsMedium — EnhancedMetaTags has dynamic URL support
17Generate dynamic OG images per fixture/player/team/leagueAll dynamic routesLow — improved social sharing CTR
18Add WebApplication JSON-LD to tool pages/prop-screener, /team-screener, /value-bets, /100-club, /outliersLow — rich search results for tools
19Use /odds-converter and /stats-explained as SEO templates for other pagesAll pages— Best-practice reference pages

Template Pages (Use as Reference)

The following pages represent best practices and should be used as templates when fixing other pages:

  1. /betting-academy/[slug] — Grade A — Perfect dynamic SSG page with full meta tags, structured data, and data-driven SEO
  2. /referee/[refereeSlug] — Grade A — Perfect dynamic SSR page with full meta tags, multiple JSON-LD schemas, and all data from SSR props
  3. /stats-explained — Grade A- — Best static page with Article + FAQPage schemas, ISR, and comprehensive meta tags
  4. /odds-converter — Grade B+ — Best tool page with 4 JSON-LD schemas (WebPage, FAQPage, BreadcrumbList, VideoObject)

On this page