Skip to content

International SEO meta tags are incorrectly rendered outside <head> into <body> #201

Description

@Liamn0244

🐛 International SEO meta tags are incorrectly rendered outside <head> into <body>

📝 Description

In the current production build, all critical SEO metadata (including <title>, <meta description>, <link rel="canonical">, and hreflang alternates) are incorrectly rendered inside or after the <body> element instead of the <head> block.
Furthermore, a client-side JavaScript snippet is being used as a temporary patch to dynamically append part of these elements back to <head>, which severely breaks server-side crawling for search engines and social media scrapers.

🔍 Proof of Evidences

Image
  1. Invalid React Attribute Naming: The rendered HTML source shows hrefLang="..." (with a capital L) instead of the standard standard lowercase HTML attribute hreflang="...". This indicates a raw JSX string print leak.
  2. <html> Lang Mismatch: The root HTML tag is set to <html lang="fr">, while the sub-region SEO alternates targets hrefLang="fr-FR". They should be properly aligned.
  3. Emergency Patch Snippet Found:
    document.querySelectorAll("body link[rel='icon'], body link[rel='apple-touch-icon']").forEach(el => document.head.appendChild(el))

🛠️ Suggested Fix

Refactor the SEO implementation in the Next.js storefront. Completely remove manual string/JSX node stitching for meta tags from the page render output. Instead, leverage the native Next.js Metadata API (export const metadata = { ... } or generateMetadata) to allow the framework to correctly hoist and validate all internationalization headers securely inside the standard structure.

Remove the JS Hack: Completely delete the client-side JavaScript patch mentioned. Once the Metadata API is adopted, Next.js will natively place all link and script tags in the head during SSR, rendering this workaround entirely obsolete and eliminating unnecessary client-side DOM manipulation.

⚠️ Critical Update: SSR vs. Hydration Mismatch affecting ALL SEO Meta Tags

After deeper investigation into the raw network payload, we found that this rendering bug affects all SEO-critical elements and introduces a major discrepancy between what is seen in DevTools and what is actually delivered by the server.

📍 Exactly Where to Look (How to Reproduce the Bug)

Do NOT use the standard Chrome Elements panel (F12) to inspect the code, as it masks the issue. Instead, developers can verify the bug using either of these two methods:

  1. Via Chrome Network Tab (Recommended):
    • Open DevTools -> Go to the Network tab -> Reload the page.
    • Click on the main document request (the initial page URL).
    • Go to the Response sub-tab (or right-click the page and select "View Page Source").
    • Search for hreflang or canonical. You will see they are delivered inside the <body> structure, far below the closed </head> tag.
  2. Via Terminal (Instant Check):
    • Run a simple curl command: curl -s https://<your-stage-url> | grep hrefLang
    • You will instantly see the raw HTML stream showing the misplaced tags with the incorrect React camelCase naming.

🔍 Why it was missed: The DevTools Illusion (Hydration)

  • In Chrome Elements: The tags appear correctly inside the <head> because React's client-side hydration process kicks in after the page loads and automatically hoists these DOM elements from the <body> back up into the <head>.
  • In the Raw Server Response: As shown in the Network tab/Page Source, the server is emitting these tags inside the <body> during SSR.

💥 The Full Scope: It is not just hreflang

This SSR leak breaks the entire SEO foundation of the page. The following critical tags are all trapped in the raw <body> response:

  • The <title> tag
  • The <meta name="description"> tag
  • The <link rel="canonical"> tag
  • All hreflang localized alternates
  • All Open Graph (og:) and Twitter Card social sharing tags

🚫 Impact on Search Engines & Scrapers

Search engine bots (like Googlebot) and social media crawlers (like Twitter, Facebook, Slack) primarily index and read the raw HTML server response before executing any client-side JavaScript. Because these tags are delivered outside the <head> in the initial byte stream, crawlers treat them as invalid syntax and completely ignore them, ruining our global search visibility and social preview cards.

🛠️ Reminder on the Fix

Please completely migrate all these tags away from manual string/JSX component assembly in the page body. Utilizing the native Next.js Metadata API (export const metadata or generateMetadata) will guarantee that the framework bundles and injects all these tags into the genuine <head> container during server-side generation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions