WordPress systems / Case 25
Reconciling custom WordPress schema with Yoast without duplicate entities
Staging and production were describing the same pages differently, and custom JSON-LD could overlap with the schema graph already generated by the SEO plugin.
Plain-English summary
What this solved for the business or user
Search engines should receive one coherent description of the business, page, location, and professional content. The work removed contradictory or duplicated structured data while keeping the information the site genuinely needed to expose.
01 / Real situation
What was happening
A schema audit found that staging and production did not output the same entity set. One environment had a simpler WebPage and Organization graph, while the other also emitted service, local-business, and professional-person entities. Some of those blocks were hard-coded in the theme footer while Yoast already generated its own base graph.
02 / Constraint
Why the obvious solution was not enough
Deleting every custom schema block would remove useful entities, but keeping independent JSON-LD scripts could duplicate WebPage or Organization data and create IDs that did not connect to Yoast's graph. The solution had to preserve the real entities while giving one system clear ownership of each shared schema piece.
03 / Implementation
How the solution works
- Inventory every JSON-LD source first: SEO plugin output, theme hooks, template-specific printers, and hard-coded footer scripts.
- Compare equivalent staging and production URLs so environment drift is visible before changing the code.
- Assign ownership for base entities. When Yoast is active, avoid printing a second independent WebPage or Organization unless there is a specific reason to replace it.
- Move custom schema printers into focused modules for entities the site genuinely needs, such as service, local-office, or professional-person data.
- Use stable @id values and relationships so custom pieces connect to the existing site graph instead of becoming unrelated JSON-LD islands.
- Gate optional entities on the content or configuration that makes them true. Do not output a LocalBusiness or professional entity only because the template supports one.
- Validate the final rendered source and test the canonical URL after cache clears. Structured-data debugging must inspect what the crawler receives, not only what the PHP function intends to return.
if (defined('WPSEO_VERSION')) {
// Let Yoast own the base WebPage / Organization graph.
add_filter('mytheme_print_custom_webpage_schema', '__return_false');
}
// Keep only genuinely additional entities in focused printers.
add_action('wp_footer', 'mytheme_print_location_entity_schema', 20);04 / Release checks
What should be verified before shipping
- Compare rendered JSON-LD on equivalent staging and production pages.
- Verify WebPage, WebSite, and Organization are not printed twice with competing IDs.
- Confirm optional service, location, or professional entities appear only on pages where the visible content supports them.
- Run Google's structured-data validation tools after clearing page and CDN caches.
- Inspect source HTML as a crawler would receive it; do not validate only a PHP array or admin preview.
05 / Result
What changed
Structured data became easier to reason about because the SEO plugin owned the base graph and the theme only added entities that represented real page content. Staging and production could then be compared against the same schema contract.
Reusable lessonStructured data is a graph, not a collection of independent scripts. Decide who owns the shared entities first, then extend that graph deliberately instead of printing another version of the same page.
References
