Fixing Structured Data Lost in a Migration
Structured data is the machine-readable description of a page: the product, its price and stock, the article and its author, the business and its address. On most sites nobody wrote it by hand. A plugin, theme or app produced it, so when the platform or theme changes, it disappears quietly and the page still looks fine.
The first sign is often lost star ratings or prices in search results weeks later. This guide shows how to record what the old site had, work out what produced it, rebuild it on the new site and confirm that search engines can read it again.
Why structured data goes missing in a move
Content is migrated deliberately: someone exports posts and products and imports them. Structured data usually is not, because it was never stored as content. It was assembled at page-render time from settings and fields by whatever software ran the old site. Change that software and the output changes with it.
| Old source | What happens in a migration |
|---|---|
| SEO plugin (for example Yoast or Rank Math on WordPress) | The plugin is not installed on the new site, or is installed with default settings, so the Organization details, logo, social profiles and article types it produced are gone or generic. |
| Theme markup, often microdata | A new theme does not include the itemprop
attributes the old one had, so product or review markup vanishes with the redesign. |
| E-commerce app or extension | Review and product apps on Shopify, WooCommerce or Magento inject their own markup. The app is not reinstalled, or the new theme lacks the block where it inserted it. |
| Tag manager injection | A JSON-LD tag in Google Tag Manager was tied to CSS selectors or data-layer variables that no longer exist on the new templates. |
| Hand-written blocks in content | Custom HTML blocks were stripped by the importer or sanitised by the new editor. |
| A headless or custom front end | Nothing is generated automatically; if the developers did not build it, there is none. |
Even when markup survives, it can carry the old site with it. Identifiers such as @id,
url, logo and image may still use the old domain or the staging
host, which quietly breaks the links between entities.
Building an inventory of the old markup
The migration audit compares structured data before and after, but to restore it you need to know exactly what each type of page carried. Work by template, not by URL: a site with forty thousand pages may have only eight templates.
- List the templates: home, category, product, article, author, location, contact, FAQ-style help pages and so on.
- Pick two or three representative URLs for each from the old crawl or a saved copy.
- For each, record the schema types present, the key properties, whether it was JSON-LD or microdata, and what produced it.
If the old site is already gone, saved crawl exports that included structured data extraction, cached copies in the Internet Archive and old Search Console enhancement reports (which list the types Google detected) will fill most of the gaps.
A short script can pull JSON-LD types from a list of addresses, which makes before-and-after comparisons quick when both versions are still reachable, for example the old site on a backup host.
import json, re, sys, requests
def types(url):
html = requests.get(url, timeout=20).text
found = []
for block in re.findall(r'<script[^>]*application/ld\+json[^>]*>(.*?)</script>', html, re.S):
try:
data = json.loads(block)
except ValueError:
found.append("INVALID JSON"); continue
items = data.get("@graph", [data]) if isinstance(data, dict) else data
found += [str(i.get("@type")) for i in items if isinstance(i, dict)]
return sorted(found)
for line in open(sys.argv[1]):
old, new = line.split()
a, b = types(old), types(new)
if a != b:
print(new, "missing:", sorted(set(a) - set(b)), "added:", sorted(set(b) - set(a)))
The script only sees JSON-LD in the HTML the server sends. Microdata and markup added by JavaScript need a crawler that renders pages, or a check in the Rich Results Test, which renders like Google.
Which lost types matter most
Not every missing block has the same effect. Prioritise by what the markup did in search results and what depends on it.
- Product and Offer on shop pages. Price, availability, shipping and returns information feed product snippets and merchant listings. Losing them is usually the most visible loss for a shop.
- Review and AggregateRating. Star ratings shown on product and some other page types. They must describe reviews visible on the page itself.
- BreadcrumbList. Replaces the raw URL with a readable trail in results on many layouts. New URL structures often change the trail, so rebuild it to match the new hierarchy.
- Article with author and dates. Helps search engines understand publication and update dates and connect content to its author.
- Organization or LocalBusiness. The site-wide description of who you are, with logo, contact details and profiles. Usually on the home or about page.
- Event, Recipe, JobPosting, Course and similar. Each drives a specific search feature and needs its required properties.
The fix, step by step
Step 1
Inventory the old markup by template
List every page template on the old site and choose two or three example URLs for each. From the pre-migration crawl, saved page source or archived copies, record which schema types each template carried, the important properties such as price, rating, author and dates, and whether the format was JSON-LD or microdata. Save the old blocks themselves in a folder, one file per template, because you will want to compare them property by property later.
Step 2
Trace what generated each block
For
each template, work out which software produced the markup: an SEO plugin, the theme, a review or product
app, a tag manager tag or hand-written HTML. Plugin output usually carries a comment or a recognisable
@id pattern, and app output often sits inside the app's own container. Knowing the source
tells you whether the fix is a setting, an app reinstall, a template change or custom code on the new
platform.
Step 3
Compare the new templates against the inventory
Run the same example pages, now at their new addresses, through the Rich Results Test or a comparison script, and note for each template which types and properties are missing, changed or new. Check the rendered result, not just the raw source, because some platforms inject markup with JavaScript. Mark each gap by priority, putting shop products, ratings and breadcrumbs first, then articles and the site-wide organisation details.
Step 4
Rebuild the missing markup in the new templates
Generate the markup in the page template from real fields, so every product, article or location gets it automatically, rather than pasting blocks into individual pages. Prefer JSON-LD rendered on the server. Use the new platform's equivalent plugin, app or theme setting where one exists, and custom template code where it does not. Make sure every value in the markup, such as price, rating and dates, matches what is visible on the page.
Step 5
Replace old-domain and staging URLs inside the markup
Search the rendered source of every template for the old domain and the
staging host. Update @id, url, logo, image, sameAs
and breadcrumb item addresses to the new canonical URLs with https and the chosen host. Keep @id
values stable from now on, and make sure that references between entities, such as an article's publisher
pointing to the organisation, use exactly the same identifier on every page.
Step 6
Validate one page of every template
Test at least one live URL per template in the Google Rich Results Test to see which features Google can find, and in the Schema Markup Validator to check the vocabulary more generally. Fix errors first, then warnings that concern properties you actually have data for. Test a second example from each template with unusual data, such as a product with no reviews or an article without an image, to catch templates that output empty or broken values.
Step 7
Watch the Search Console enhancement reports
In the new Search Console property, open the reports under Shopping and Enhancements, such as Product snippets, Merchant listings, Breadcrumbs and Review snippets, and check that valid item counts climb back towards the old property's figures as pages are recrawled. Investigate any new error type straight away, because it usually affects a whole template. Rerun the Site Migration Audit to confirm the structured data comparison no longer shows missing types.
Getting identifiers and URLs right
Modern markup often uses a connected graph: the WebSite, the Organization, the WebPage and the Article
each have an @id, and they refer to each other. After a migration those identifiers must
be rebuilt on the new domain, and they must be identical everywhere they are used.
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@graph": [
{
"@type": "Organization",
"@id": "https://www.example.co.uk/#organization",
"name": "Brightwater Garden Supplies",
"url": "https://www.example.co.uk/",
"logo": "https://www.example.co.uk/images/logo-512.png"
},
{
"@type": "WebSite",
"@id": "https://www.example.co.uk/#website",
"url": "https://www.example.co.uk/",
"name": "Brightwater Garden Supplies",
"publisher": { "@id": "https://www.example.co.uk/#organization" }
},
{
"@type": "BreadcrumbList",
"@id": "https://www.example.co.uk/tools/pruning/#breadcrumb",
"itemListElement": [
{ "@type": "ListItem", "position": 1, "name": "Home", "item": "https://www.example.co.uk/" },
{ "@type": "ListItem", "position": 2, "name": "Tools", "item": "https://www.example.co.uk/tools/" },
{ "@type": "ListItem", "position": 3, "name": "Pruning", "item": "https://www.example.co.uk/tools/pruning/" }
]
}
]
}
</script>
- Use the canonical form of every address. An
itemthat redirects or alogoon the old host still works in a browser but no longer describes the new site cleanly. - Rebuild breadcrumbs from the new hierarchy. If categories were renamed or flattened, the old trail no longer matches the visible breadcrumb.
- Update sameAs profiles only if they changed. Social profile addresses do not usually move, but check any that used the old brand name.
Where to rebuild it, by platform
WordPress to WordPress (new theme)
Keep the same SEO plugin and export its settings from the old site, so the organisation details, logo and content type settings carry over. Check that the new theme does not add its own conflicting markup; two Organization or Product blocks from different sources is a common result of a redesign.
Moving to Shopify
Themes add basic Product markup, but the level of detail varies. Reviews usually come from an app, which must be reinstalled and connected to the theme. Compare a product page against the inventory, especially offers, availability, brand and identifiers such as GTIN.
Headless and custom front ends
Build a small set of functions that turn CMS fields into JSON-LD for each template, and render the result on the server. Treat the inventory from step one as the specification for that work. The headless migration guide covers rendering checks in more detail.
Keeping it from happening again
- Keep the inventory. The template-by-template record from step one becomes the specification for the next redesign or platform change.
- Test markup before launch. Add structured data comparison to the migration test plan alongside redirects and content, using staging URLs.
- Watch for changes after launch. Theme updates, app updates and CMS upgrades can remove markup long after a migration. A regular comparison, such as the one described in the Schema Drift Guide, catches these early.
- Own the source. Document which plugin, app or template produces each type, so no one removes it without realising.
1. Why did my star ratings disappear from Google after the redesign?
Most often the review markup came from the old theme or a review app, and the new theme or platform does not output it. Check a product page with the Rich Results Test: if no Review or AggregateRating appears, the markup is missing. If it is present but not shown, Google may still be recrawling, or it may have chosen not to show stars for that page. Rich results are never guaranteed, even with valid markup, but they cannot appear without it.
2. How long does it take for rich results to come back after fixing schema?
It depends on how quickly Google recrawls the affected pages. Important pages that are crawled often may show updated results within days, while deeper pages can take weeks. The Search Console enhancement reports update as pages are recrawled, so watch the valid item counts rise. Requesting indexing for a few key URLs through URL Inspection can help for those pages, but it is not a way to speed up a whole site.
3. Should I use the same schema plugin on the new site?
If the new site runs on the same CMS, keeping the same plugin is usually the simplest way to preserve the markup, because its settings can often be exported and imported. If you change platform, there is no need to copy the tool, only the output. Use the inventory of the old markup as the target, then choose whatever the new platform does best, whether that is a built-in feature, an app, or template code.
4. Do the @id values in my schema have to change when the domain changes?
They should. An @id is an identifier, and most sites build it from the page URL, such as the home page address followed by #organization. After a domain move, identifiers on the old domain still work as labels, but they no longer match the canonical URLs of the new site and are easily mixed with new ones. Choose new identifiers on the new domain, use them consistently on every page, and do not change them again.
5. Is microdata from my old theme still acceptable, or must I switch to JSON-LD?
Google supports JSON-LD, microdata and RDFa, so microdata is still valid. JSON-LD is usually easier to maintain after a migration because it sits in one block that the template produces, separate from the visible HTML, so a redesign is less likely to break it. If you are rebuilding the markup anyway, JSON-LD is the sensible choice. Avoid outputting the same entity in both formats at once, because search engines then see two separate descriptions of one thing.
6. The Rich Results Test shows my markup but Search Console reports are empty. Why?
Search Console reports only include pages Google has crawled since the markup appeared, and a new property starts with no history. After a migration, the enhancement reports in the new property fill up gradually as pages are recrawled. If the test is clean and weeks pass with no growth, check that the pages are indexed, that they are not blocked or noindexed, and that the markup is in the HTML Google renders rather than added after a user action.
The Site Migration Audit compares your pre-migration crawl with the live site and verifies that structured data survived the move, so you can see which templates lost their markup.
Open Migration Audit Fixes โ