ServicesAI Audit
← Back to Blog

Where Should JSON-LD Schema Go: Head or Body?

JSON-LDSchema MarkupTechnical SEOAI VisibilityStructured DataHTML

The short answer - and why it is not that simple

Google officially says it does not mind. You can place a <script type="application/ld+json"> block in either the <head> or the <body> of your HTML and Google's crawler will find it either way. That has been confirmed in their developer documentation for years. So technically, both work.

But "technically works" and "best practice" are two different things. Where you place your JSON-LD has real implications for how reliably it is parsed, how quickly it is read by crawlers and AI systems, and how easy it is to maintain over time. The choice matters more than most people realise.

What Google's documentation actually says

Google's structured data guidelines state that JSON-LD can appear in the <head> or <body> of a page. Their Rich Results Test will pick it up from either location. So if you have a developer who has dropped schema into the body for convenience, that is not an automatic disqualification.

However, Google's own examples almost always show JSON-LD inside the <head>. That is not an accident. When a team writes documentation examples, they default to the pattern they consider cleaner and more reliable. The implicit recommendation is clear, even if the explicit rule is flexible.

Schema.org itself has no strong opinion on placement. The spec is concerned with the vocabulary and structure of the data, not where the script tag sits in the DOM. So any "Schema.org says put it in the head" advice you read online is someone else's interpretation, not a direct spec requirement.

Why the head is the better choice in practice

There are three solid reasons to put JSON-LD in the <head>, and none of them are just pedantry.

1. Crawlers read the head first

Search engine crawlers, and the AI systems that index web content, process HTML from top to bottom. When structured data appears in the <head>, it is encountered before any body content, images, scripts, or other resources that might slow down or interrupt parsing. For pages with heavy JavaScript, large images, or lazy-loaded content, this can make a meaningful difference to whether your schema is reliably captured on every crawl.

AI retrieval systems in particular, including those powering ChatGPT's browsing, Perplexity, and Google's AI Overviews, do not always render a full page the way a browser does. Some fetch the raw HTML and parse it quickly. If your JSON-LD is buried deep in the body after several hundred lines of markup, there is a higher chance it gets missed or deprioritised in that kind of lightweight fetch.

2. It separates metadata from content cleanly

JSON-LD is metadata. It describes the page; it is not part of the visible content. The <head> is precisely where metadata lives: your title tag, meta description, canonical tag, Open Graph tags, and so on. Putting JSON-LD there keeps your architecture consistent and logical.

When schema ends up in the body, it usually means it was added as an afterthought, often injected by a plugin or CMS template that inserts it near the content it describes. That is understandable, but it creates maintenance headaches. If you are auditing a site and trying to find all structured data, you want to know it is always in one place.

3. It avoids conflicts with JavaScript rendering

Modern sites often use JavaScript frameworks that manipulate the DOM after page load. If your JSON-LD is in the body and sits near components that get rewritten or moved by JavaScript, there is a small but real risk of the schema being stripped, duplicated, or corrupted during rendering. Keeping it in the <head>, where JavaScript frameworks are less likely to touch it, reduces that risk considerably.

When body placement is acceptable

There are genuine scenarios where body placement makes sense or is unavoidable.

Dynamically generated schema tied to specific content blocks

Some CMS platforms, particularly page builders and headless setups, generate schema at the component level. A product description block might output its own Product schema inline. A review component might emit Review schema right next to the review content. In these cases, body placement is a side effect of the architecture, and fixing it requires more than moving a script tag.

This is totally fine from a technical standpoint. Google handles it. The key is that the schema is still well-formed and complete. Placement is secondary to correctness.

Tag managers and third-party injections

Google Tag Manager injects scripts into the body by design. If you are deploying JSON-LD via GTM, it will land in the body. Again, this works, and many large sites run perfectly valid structured data this way. The trade-off is that GTM-injected schema requires JavaScript to execute, which means it will not be seen by crawlers that do not render JavaScript. For critical schema types like Product, FAQPage, or BreadcrumbList, hardcoding in the head is more reliable than relying on GTM.

If you want to understand more about adding JSON-LD without risking breakage, this post on how to add JSON-LD schema to your website without breaking anything covers the practical steps in detail.

Shopify and WordPress: what actually happens

Most people asking this question are not hand-coding HTML. They are working with Shopify or WordPress, and the platform is making placement decisions for them.

Shopify

Shopify themes typically output JSON-LD in the body, often near the bottom of the page or within specific sections. The Dawn theme, for example, places product schema within the product section template. This is body placement by default. It works for Google's Rich Results, but it is not ideal for AI crawlers that do minimal rendering.

If you are serious about AI visibility on Shopify, hardcoding your core schema blocks (particularly Organization, WebSite, and Product) into the theme's layout/theme.liquid file in the <head> section gives you more reliable coverage. This is one of the first things we do at FlinnSchema when optimising a Shopify store for AI search.

WordPress

WordPress plugins like Yoast SEO, Rank Math, and Schema Pro output JSON-LD in the <head> by default. That is good. Where things get messy is when multiple plugins are active simultaneously, each injecting their own schema blocks, sometimes with conflicting or duplicated data. You can end up with two Organization blocks or three WebPage blocks, all in the head, confusing rather than helping crawlers.

Having one clean, well-structured JSON-LD block in the head is better than three competing ones, regardless of where they sit.

Multiple schema blocks: how to handle them

You can absolutely have more than one JSON-LD script block on a single page. A product page might legitimately have Product, BreadcrumbList, and FAQPage schema all present at once. There is no rule against multiple blocks.

However, there are two things to avoid. First, do not put the same type twice with conflicting data. If you have two Organization blocks with different names or URLs, crawlers will not know which to trust. Second, if you are using multiple blocks, keep them all in the same location, ideally all in the head, so they are easy to audit and maintain.

A single JSON-LD block that uses a @graph array is often the cleanest approach for pages with multiple schema types. It consolidates everything into one script tag, reduces HTTP overhead, and makes your structured data far easier to read and debug.

If you are not sure which schema types your e-commerce site should be using in the first place, take a look at which schema types every e-commerce site should have.

Testing your placement

Once your JSON-LD is in place, you need to confirm it is being read correctly regardless of where it sits.

Google's Rich Results Test at search.google.com/test/rich-results is the obvious starting point. Paste your URL or your raw HTML and it will show you every schema block it detects and any errors or warnings. If it finds your schema, placement is not an issue for Google.

For AI visibility specifically, the picture is more complex. AI systems like Perplexity and ChatGPT do not have a public schema testing tool. What you can do is check that your schema appears in the raw HTML source (not just after JavaScript renders) by using "View Page Source" in your browser rather than inspecting the DOM. If your JSON-LD is visible in the raw source, it will be accessible to crawlers that do not execute JavaScript.

At FlinnSchema's free AI visibility audit, placement issues are one of the first things we flag, particularly for Shopify stores where body-placed schema combined with JavaScript rendering creates silent gaps in AI coverage.

The practical recommendation

Put JSON-LD in the <head>. It is where metadata belongs, it is encountered first by crawlers, and it is less likely to be disrupted by JavaScript or CMS rendering. If your platform makes head placement difficult, body placement will still work for Google, but you should be aware of the trade-offs for AI search coverage.

If you are using GTM for schema deployment, consider whether the convenience is worth the reliability cost for your most important schema types. For anything that directly affects how AI engines describe your business, hardcoding in the head is the safer path.

And whatever you do, keep it consistent. One reliable location, correct markup, and a regular audit process will serve you far better than chasing marginal placement optimisations on a site with messy or incomplete schema.

Frequently Asked Questions

Does Google treat head and body JSON-LD differently?

No, Google processes JSON-LD from both locations and its Rich Results Test will detect schema in either the <head> or <body>. That said, head placement is considered best practice because it is encountered earlier in the HTML parsing process and is less susceptible to JavaScript interference.

Can I have multiple JSON-LD script tags on one page?

Yes. Multiple script tags are perfectly valid. You might have Product, BreadcrumbList, and FAQPage schema on a single product page, for example. Just avoid having two blocks of the same type with conflicting data. Using a single @graph array to combine multiple types into one block is often the cleanest approach.

Will JSON-LD injected by Google Tag Manager work for AI search?

GTM-injected JSON-LD requires JavaScript to execute before it appears in the DOM. AI crawlers that fetch raw HTML without rendering JavaScript will not see it. For schema that affects how AI engines understand and describe your business, hardcoding the JSON-LD directly into your HTML is more reliable than GTM deployment.

How do I check if my JSON-LD is in the raw HTML versus injected by JavaScript?

In your browser, right-click the page and choose "View Page Source" (not "Inspect"). This shows the raw HTML as delivered by the server. If your JSON-LD script block appears there, it is in the raw HTML and will be visible to all crawlers. If it only appears in the browser's developer tools inspector, it is JavaScript-injected and will not be seen by crawlers that do not render JS.

Want to check your AI visibility?

Run a free audit on your website and see how visible you are to ChatGPT, Perplexity, and other AI search engines.

Run Free Audit