Indexing and technical SEO guide
Find Duplicate SEO Tags From WordPress Themes and Plugins
Count the title, canonical, robots, and description tags in the public HTML, find which theme or plugin prints each copy, and leave one owner per tag.
On this page
Fetch the public page while signed out, count each SEO tag in the <head>, and give every tag type exactly one owner. A page should normally print one <title>, one canonical link, one description, and one set of robots directives. When you find two, the fix is to switch off the duplicate output in the component that should not own it, not to delete theme or plugin features you have not traced.
Duplicates matter for different reasons. Two canonical links that name different URLs send contradictory preferences. Google’s canonicalization guidance, checked September 27, 2026, says not to specify different URLs as canonical for the same page using different techniques. For robots rules, Google’s robots meta tag documentation, checked September 27, 2026, says that when rules conflict the more restrictive rule applies. A stray noindex from a theme therefore wins over an index from your SEO plugin.
Inventory the tags in the public head
Work from what a visitor receives, not from the settings screens. Signed-in administrators often see different HTML because caches are bypassed and toolbars are added.
Run these against one URL of each template type you use: the home page, a post, a page, a category archive, and any custom post type. The address is illustrative; use your own.
URL="https://garden.example/garden-design/"
# Count the tags that should appear once
curl -s "$URL" | grep -io '<title[^>]*>'
curl -s "$URL" | grep -io '<link[^>]*rel=["'\'']canonical["'\''][^>]*>'
curl -s "$URL" | grep -io '<meta[^>]*name=["'\'']robots["'\''][^>]*>'
curl -s "$URL" | grep -io '<meta[^>]*name=["'\'']description["'\''][^>]*>'
curl -s "$URL" | grep -io '<meta[^>]*property=["'\'']og:url["'\''][^>]*>'
# Robots rules can also arrive as a response header
curl -sI "$URL" | grep -i 'x-robots-tag'
The grep -o patterns print each tag on its own line, so a second line is a second copy. The commands read the whole page, so an inline SVG icon with its own <title> in the body also matches; check where each match sits before calling it a duplicate. A tag split across several lines can escape the pattern; if the counts look wrong, open view-source: followed by the URL in your browser and search for canonical and robots by hand.
Record what you find in a small table before changing anything:
| Tag | Copies | Values | Suspected source |
|---|---|---|---|
<title> |
2 | “Garden Design”, “Garden Design / Garden Notes” | Theme header, SEO plugin |
rel="canonical" |
2 | /garden-design/, /garden-design/?amp=1 |
SEO plugin, AMP plugin |
robots meta |
1 | index, follow, max-image-preview:large |
WordPress core pipeline |
X-Robots-Tag |
1 | noindex |
Server or security plugin |
The rows above are an invented illustration of the format. Your own rows are the evidence.
Also compare the raw source with the rendered DOM in your browser’s developer tools. Google’s canonicalization troubleshooting page, checked September 27, 2026, notes that some CMSs and plugins misuse canonical techniques and suggests checking the HTML with developer tools. A tag that appears only in the rendered DOM was added by JavaScript, which points to a script rather than a PHP template.
Know where WordPress tags usually come from
Most duplicate tags come from a small number of places. Knowing them narrows the search.
| Source | What it tends to print | How to recognize it |
|---|---|---|
| WordPress core | <title> through the title-tag feature, one robots meta tag through wp_robots(), a canonical on singular views such as posts and pages |
Output appears wherever the theme calls wp_head() |
| An SEO plugin | Title, description, canonical, robots directives, social tags, often schema | Often wrapped in a plugin comment, or printed as one block |
| The theme | A hard-coded <title> or description in header.php, social tags in a theme options panel |
A hard-coded tag sits outside the wp_head() block in the template file |
| A second SEO or social plugin | A full second set of tags | Two blocks with different formatting |
| Page builders, AMP, and translation plugins | Canonical and alternate links for their own URL variants | The duplicate names a variant URL |
| Server, CDN, or security rules | X-Robots-Tag headers |
Visible in the response headers (curl -I), not in the HTML |
WordPress’s add_theme_support() reference, checked September 27, 2026, describes the title-tag feature as letting plugins and themes manage the document title in place of wp_title(). A theme that still hard-codes <title> in header.php and also supports title-tag prints two titles. The wp_robots() reference, checked September 27, 2026, describes a single robots meta tag assembled from the wp_robots filter since WordPress 5.7, so a plugin that prints its own robots tag instead of using that filter can create a second one. The rel_canonical() reference, checked September 27, 2026, says core outputs its canonical for singular queries only.
To confirm a theme source, search the theme folder for hard-coded tags:
grep -rniE '<title|rel="canonical"|name="robots"|name="description"|og:' wp-content/themes/your-theme/
Isolate each emitter on a staging copy
Do not switch plugins off on the live site to find the culprit. Use a staging copy, or the Health Check & Troubleshooting plugin, which WordPress.org describes as giving you a session with all plugins disabled and a default theme, only for your user until you disable it or log out. Checked September 27, 2026. Because troubleshooting mode affects only your session, inspect the page source while signed in during that test, not with anonymous curl.
- Record the tag table for one URL with everything active.
- Switch to a default theme with the same plugins. If a duplicate disappears, the theme printed it.
- Restore the theme and turn off plugins one at a time, starting with anything that touches SEO, social sharing, AMP, translation, or page building. Recount after each change.
- When a copy disappears, note the plugin, its version, and the page types affected. Repeat the count on a second template type, because some plugins print tags only on posts or only on archives.
- Check the response headers again with the stack restored. A header-level
X-Robots-Tagusually traces to server configuration, a CDN rule, or a plugin rather than a theme template.
Write down versions as you go. A later update can bring the duplicate back, and a dated record makes that easy to spot.
Keep one owner for each tag
Choose the component that should own each tag type, then turn off that output everywhere else through a setting, a theme option, or a child theme template. Removing a whole plugin to stop one tag can take away features the site depends on, such as a form or a translation workflow.
- Two SEO plugins active. Keep one. Two active SEO plugins can each print their own set of tags unless one is configured not to. If you are moving from one to another, follow a sequenced plugin switch rather than leaving both on.
- Theme title or description. Remove the hard-coded tags in a child theme’s
header.phpand keep thewp_head()call. Core’s_wp_render_title_tag(), checked September 27, 2026, prints a title only when the theme supportstitle-tag, so if yours does not, addadd_theme_support( 'title-tag' );to the child theme’sfunctions.phpin the same change or the page is left with no title. Some themes also have an option to turn off their own SEO fields. - Theme social tags. Turn off the theme’s Open Graph output if your SEO plugin prints social tags, or the reverse. The Open Graph protocol, checked September 27, 2026, treats repeated tags as multiple values and gives the first one preference in conflicts, so the component that prints first can decide the preview image.
- Variant canonicals. AMP, translation, and builder plugins may print canonicals for their own URL variants. Decide whether that plugin or the SEO plugin owns the variant, and check that each variant points where you intend.
- Server
X-Robots-Tag. Ask whoever controls the server or CDN to scope the rule. A header that saysnoindexoverrides a page-levelindexunder Google’s more-restrictive rule.
WP Visibility supplies its title through WordPress’s pre_get_document_title filter, which core prints for themes that support title-tag. It sends its robots directives through the core wp_robots pipeline and prints its canonical, description, and social tags in one block at wp_head priority 1. It removes WordPress core’s own canonical output while it runs. It does not remove tags a theme writes directly into its templates, so a hard-coded theme title still needs the child theme fix above. That is the behavior of the 2.10.3 release. Its noindex settings, and the robots directives they produce, are described in Keep a Page Out of Search.
Compare templates after the fix
Rerun the same commands on every URL in your sample and fill in a second column in the table. The fix is done when:
- each template type prints no more than one
<title>, canonical, description, and robots meta tag; - the canonical names the page’s own preferred URL, not a variant or the home page;
- no
X-Robots-Tagheader withnoindexornoneappears on pages that should be indexable; - social tags appear once each, with the image and URL you expect.
Clear page caches before checking, or you may be counting tags from the old response. If a count still disagrees after a purge, see the cache tracing guide.
If the one remaining canonical points to the wrong address, that is a separate problem: the output owner is settled, but its destination is wrong. The URL and canonical chapter of the field guide covers choosing the preferred address. After deploying, the indexed result in Search Console’s URL Inspection tool, checked September 27, 2026, reports the user-declared canonical from the last crawled version of the page, so allow for a recrawl before expecting it to match. The live test in the same tool fetches the page in real time.
