Next.js Discord

Discord Forum

Next.js 16 Beta: Metadata tags showing up in body instead of head (breaking SEO)

Unanswered
Spaghetti posted this in #help-forum
Open in Discord
Hey everyone, need some help with a frustrating metadata issue 😅

So I've got Next.js 16 beta (React 19) and all my metadata is ending up in the <body> instead of the <head> in the server HTML. Client-side after hydration it fixes itself, but that doesn't help search engines.

You can see it live:
- Repo: https://github.com/Kareem-AEz/TicketBounty
- Site: https://ticket-bounty-pi.vercel.app/
- Go to any page except /, refresh, view source - metadata is in the body 💀

Setup:
// app/layout.tsx
export const metadata = {
  title: { template: "%s | Site", default: "Site Name" },
  metadataBase: new URL("https://mysite.com"),
};

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  );
}


Pretty standard stuff right? No manual <head> tags, using the Metadata API properly.

The problem:

When I check the raw HTML (both dev and production, view source):
- <head>: charset, viewport, next-size-adjust (3 tags)
- <body>: title, description, og:title, og:image, twitter:card... everything else (15+ tags)

After React hydrates, everything's in the head where it should be. But Google/Twitter/etc don't see that.

The bizarre part: Homepage (/) works perfectly. Every other route (/tickets, /ticket/123, etc.) is broken on refresh.

Already tried:
- ✅ Killing and restarting dev server (fully, not hot reload)
- ✅ Removing Turbopack
- ✅ Both <head></head> and <head /> and no head at all
- ✅ Made sure metadataBase is set
- ✅ Disabled React Compiler
- ✅ No next/head imports anywhere
- ✅ Rolled back to Next 15.5.5 stable - still happens
- ✅ Production build (npm run build) - still happens
- ✅ Deployed to Vercel - still happens in production

It's happening on all pages except the homepage (/). Homepage works perfectly every time.

At first I thought it was a beta bug, but it's happening in stable, in production, and consistently across all environments. The docs say metadata should be in the "initial streamed response" but it's clearly ending up in the body for non-root routes.

Anyone else seeing this? The live site makes it easy to verify - just visit https://ticket-bounty-pi.vercel.app/tickets and view source.

Would really appreciate any pointers, this is completely blocking SEO 🙏

Verification script if you want to check yours:
fetch('/tickets').then(r => r.text()).then(html => {
  const head = html.split('</head>')[0];
  const body = html.split('<body')[1];
  console.log({
    headMeta: (head.match(/<meta/gi) || []).length,
    bodyMeta: (body.match(/<meta/gi) || []).length
  });
});


Thanks in advance!

11 Replies

that is a weird bug..
this is what happen if you try to access ur page via browser
using this user agent
but if you add bot in the user agent, it shows the metadata correctly
what do you think @Spaghetti ?
is it still really block SEO?
Pacific sand lance
it's not bug
damn , today i learned!