The layers that write your HTML
Every WordPress page is a stack of output from different owners. Each layer is fixed in its own way,
and an edit in the wrong place is usually lost.
| Layer | Typical errors | Where the fix belongs |
Classic theme PHP templates (header.php, footer.php, single.php) | Unclosed
wrappers, missing lang or charset, obsolete attributes, type attributes on
scripts | A child theme that overrides the file |
Block theme templates and template parts (templates/*.html, parts/*.html) | Headings
or groups nested in the wrong element, duplicate ids in headers | The Site Editor, or a child theme
copy of the file |
| WordPress core filters | Stray or empty <p> tags around shortcodes and
divs from wpautop | Content structure, shortcode code or a narrowly targeted filter |
| Plugins | Duplicate ids, invalid attributes, nested forms, inline scripts with old type
values | Plugin settings, an update, the developer, or a filter the plugin provides |
| Page builders | Deeply nested wrappers, repeated custom ids, headings inside links | The
builder's element settings |
| Post and page content | Obsolete tags, unclosed lists, pasted document fragments | The
editor, or a bulk search and replace |
| Widgets and menus | Duplicate menu item ids when one menu appears twice, invalid Custom
HTML widgets | Widget and menu settings, or a small filter |
โ ๏ธ Never fix errors by editing a parent theme's or a plugin's files directly. The
next update replaces those files and your fix disappears without warning. Use a child theme, the Site
Editor, settings or filters instead.
Pinning each error to its layer
The fastest method is subtraction on a staging copy: remove a layer, validate again, and see which errors vanish.
Theme or not?
Switch the staging site to a current default theme, such as the latest Twenty-something theme, and
validate the same URLs. Errors that disappear belonged to your theme. Errors that stay come from plugins,
core output or content.
Which plugin?
The Health Check & Troubleshooting plugin, published on WordPress.org, has a troubleshooting mode
that disables all plugins and switches theme for your logged-in session only, so visitors
see nothing different. Enable plugins one at a time and re-check. Because an external checker visits
as a logged-out visitor, it will not see your troubleshooting session; use your browser's view source
and a validator's direct input option, or do the test on staging.
Content or template?
Validate two posts that use the same template. An error at the same point on both is template or plugin
output; an error on one post only is that post's content. The line number in the validator, matched against
the page source, tells you whether the error falls inside the post content container, usually an element
with a class such as entry-content or wp-block-post-content.
Reading WordPress's own clues
- Stylesheet and script tags carry ids ending in
-css or -js made from their
registered handle, which usually names the plugin, for example contact-form-x-css.
- Block markup has classes starting
wp-block- followed by the block name, and third-party
blocks use their plugin's prefix.
- Many plugins print an HTML comment, such as a version line, before their output.
Fixing theme markup without losing it at the next update
Classic themes: a child theme
A child theme is a folder with a style.css that names the parent in its header and, usually,
a functions.php. Any template file you copy into the child theme, such as header.php,
is used instead of the parent's copy. Copy only the files you need to change, fix the markup there, and
note the parent version you copied from, because parent updates will not reach your copy.
Many older-theme warnings are cleared in one line. Declaring HTML5 support tells WordPress to output
modern markup for search forms, comments, galleries, captions and, since WordPress 5.3, script and style
tags without the redundant type attribute:
add_action( 'after_setup_theme', function () {
add_theme_support( 'html5', array(
'search-form', 'comment-form', 'comment-list',
'gallery', 'caption', 'script', 'style', 'navigation-widgets',
) );
}, 20 );
Check the parent's header.php for three things while you are there: language_attributes()
inside the <html> tag, <meta charset="<?php bloginfo( 'charset' ); ?>">
early in the head, and a call to wp_body_open() straight after the opening body tag, which
plugins use to insert content in a valid place.
Block themes: the Site Editor
Block themes build pages from HTML template files made of block markup. Changes made in the Site Editor
(Appearance, then Editor) are saved in the database and take priority over the theme's files, so they
survive theme updates. That is the simplest place to fix a heading nested in the wrong group or a template
part added twice. The downside is that a later improvement to that template in the theme will not show
until you reset your customised version, so keep a note of what you changed.
Automatic paragraphs, shortcodes and the classic editor
wpautop is a core filter that turns double line breaks in post content into <p>
tags and single ones into <br>. It was written for simple prose and struggles with
markup: a shortcode on its own line that outputs a div ends up as <p><div>โฆ</div></p>,
which the validator reports as a stray end tag, and blank lines inside hand-written HTML produce empty
paragraphs.
WordPress skips wpautop for content made of blocks, so this is mostly a problem for classic
editor content, the Classic block, widgets and older posts. Ways to deal with it, from safest to broadest:
- Fix the content structure. Put each shortcode on its own line with blank lines above
and below. WordPress's
shortcode_unautop filter then removes the paragraph wrapped around
a shortcode that stands alone.
- Fix the shortcode. If you wrote the shortcode, return markup without surrounding
whitespace, and when it wraps enclosed content use
do_shortcode() on that content rather
than printing it raw.
- Convert the post to blocks. Converting a classic post to blocks (the Classic block's
"Convert to blocks" option) removes it from
wpautop altogether. Review the result, because
complex HTML can convert imperfectly.
- Remove the filter only where needed.
remove_filter( 'the_content', 'wpautop' )
switches it off everywhere, which breaks the paragraphs in every classic post that relied on it. If you
must, do it conditionally for a specific post type.
Block validation is a different thing
The editor message "This block contains unexpected or invalid content" is not an HTML validation error.
It means the block's saved markup no longer matches what the block's code would produce, often after
a plugin update or a hand edit in the code editor. Use the block's "Attempt recovery" option, then validate
the page as normal.
Plugin and page builder output
Plugin markup belongs to the plugin, so the order of attack is: settings, update, report, filter, replace.
- Settings. Many errors come from configuration: a form placed twice, repeating every
field id; a custom id typed into a page builder element's advanced tab and then duplicated with the section;
a widget placed in both a desktop and a mobile sidebar.
- Update. Validation fixes are often included in routine releases. Update on staging first and re-check.
- Report. Open a support topic on the plugin's WordPress.org page or with its vendor,
with the URL, the validator message and the plugin version.
- Filter. Well-built plugins expose filters for their output. Use them in your child
theme's
functions.php or a small site-specific plugin, never by editing the plugin's files.
Rewriting another plugin's whole output with output buffering and string replacement is fragile; avoid
it.
- Replace. If a plugin's markup breaks forms or labels and the developer will not
fix it, compare alternatives. Fewer plugins also means fewer sources of errors.
Page builders deserve a special note. They wrap each element in several containers, so the page source
is long, but the markup is usually valid. The errors tend to come from content placed inside the builder:
a heading widget set to render as a link around a block, a Custom HTML element with an unclosed tag,
or a text element pasted from a document. Fix those in the element itself.
Menus printed in two theme locations, such as a desktop and a mobile header, repeat the menu-item-
ids WordPress adds to each item. The duplicate
ids guide covers that case and the filter that removes them.
The fix, step by step
Step 1
Set up a staging copy and a baseline
report
Create a staging copy of the site, which most managed WordPress hosts offer in one click,
or use a local copy. Run the HTML Checker on one live URL of each template, such as the home page, a
post, a page, an archive, a product if you run WooCommerce, and the contact page. Save the error list
as your baseline, so you can tell which layer each change fixed and spot anything new that a change introduces.
Step 2
Switch to a default theme to split
theme errors from the rest
On staging, activate a current default WordPress theme and validate
the same URLs again. Mark every error that disappeared as a theme error. Everything that remains comes
from plugins, core filters or content. This one comparison usually sorts most of the list, and it prevents
the common mistake of rewriting theme templates to fix an error that a plugin was printing all along.
Switch back to your own theme when you are done.
Step 3
Isolate plugin errors in troubleshooting
mode
Install the Health Check & Troubleshooting plugin and enter troubleshooting mode, which
disables plugins for your session only. Enable plugins one at a time, view the page source and validate
it by direct input after each one. Note which plugin introduces which error. On a large site, enable
plugins in halves to narrow the culprit down quickly. Remember to leave troubleshooting mode afterwards
to restore your normal session.
Step 4
Override theme templates in a child
theme or the Site Editor
For a classic theme, create a child theme if you do not have one, copy
only the template files that contain errors into it, and fix the markup in the copies. Add HTML5 theme
support to clear old script and style warnings. For a block theme, correct the template or template part
in the Site Editor. Record the parent theme version and every file or template you changed, so future
updates can be compared.
Step 5
Deal with automatic paragraphs
and shortcodes
Where the validator reports stray end tags or empty paragraphs around shortcodes
and embedded HTML, look at the post in the classic or code editor. Put each shortcode on its own line
with a blank line either side, remove blank lines inside hand-written HTML, or convert the post to blocks.
Only remove the wpautop filter as a last resort, and then only for a specific post type
rather than across the whole site.
Step 6
Fix plugin output through settings,
updates or filters
For each plugin error found in step 3, check the plugin's settings for duplicated
placements and custom ids, then update it on staging and validate again. If the error remains, use a
filter the plugin documents, placed in your child theme or a small site plugin, or report it to the developer
with the URL and message. Replace a plugin whose output breaks forms or labels and cannot be fixed.
Step 7
Clean stored post content and re-validate
For
content errors, open each affected post and fix it in the editor's code view, or use WP-CLI's wp search-replace
with --dry-run first for patterns repeated across many posts, after taking a database backup.
Then validate the baseline URLs again on staging, compare with the original report, click through menus
and forms, and deploy the child theme and settings changes to the live site.
Content habits that cause most post errors
- Pasting from Google Docs or Word. Pasted text can carry wrapper tags with generated
ids, spans, font tags and empty paragraphs. The block editor cleans much of this, but the classic editor
and Custom HTML blocks keep it. Paste as plain text and format with the editor.
- Custom HTML blocks. Code pasted into a Custom HTML block is printed exactly as written.
Preview it, and validate the page, every time one is added.
- Switching editor tabs. In the classic editor, moving between the Visual and Text
tabs lets the visual editor rewrite hand-written HTML. Edit complex markup in one mode only.
- Old imported content. Posts imported from other platforms often contain obsolete
tags and table layouts. The deprecated
HTML guide shows the replacements and a safe way to find them in the database.
- User roles. WordPress filters the HTML that users without the unfiltered HTML capability
can save, which can strip attributes an author thought they had added. If markup changes after saving,
the author's role may be the reason.
Caching and minification
Performance plugins that minify HTML can change the markup the validator sees, for example by removing
optional closing tags or quotes. That output is usually still valid, but if an error only appears with
minification on, test with it off to confirm where it comes from before changing the theme. For WordPress
accessibility issues beyond validation, see the WordPress
accessibility guide.
Questions and answers
1. Will fixing HTML errors in WordPress improve my rankings?
Not directly. Google has said that valid HTML is not a ranking factor, and WordPress
sites rank well with some validation errors. The errors worth fixing are the ones that change how the
page works: a theme wrapper left unclosed so the sidebar ends up inside the article, a plugin form nested
inside another form, or labels attached to the wrong fields. Those affect visitors and assistive technology.
Clearing warnings such as redundant type attributes is tidy but changes nothing that search engines see.
2. Why are there empty p tags all over my WordPress pages?
They are almost always produced by wpautop, the core filter that converts line breaks
in classic content into paragraphs. Blank lines between shortcodes or inside hand-written HTML become
empty paragraph tags, and shortcodes that output a div can end up wrapped in a paragraph. Put each shortcode
on its own line with a blank line either side, remove stray blank lines inside HTML, or convert the post
to blocks, which WordPress does not run wpautop on.
3. Is it safe to edit my theme's header.php to fix an error?
Not in the parent theme. Any change to a parent theme's files is overwritten at its
next update, usually without any warning. Create a child theme, copy header.php into it, and make the
fix in the copy, which WordPress will use instead. For a block theme, make the change in the Site Editor,
which stores it in the database. Either way, note the parent theme version you started from, because
later improvements to that file will not reach your copy.
4. How can I tell which plugin is causing a validation error?
Look for clues in the page source first: stylesheet and script ids often contain the
plugin's handle, block classes use the plugin's prefix, and many plugins print an identifying HTML comment.
If that is not enough, use the Health Check and Troubleshooting plugin's troubleshooting mode, which
disables plugins for your session only, and re-enable them one at a time while validating the page source.
On staging, you can simply deactivate plugins in halves until the error disappears.
5. Why does the validator warn about type text/javascript on my scripts?
Because the type attribute is unnecessary for JavaScript in current HTML, so the validator
suggests removing it. It is a warning, not an error, and makes no difference to how the page works. WordPress
stops adding it to the scripts and styles it prints once the theme declares HTML5 support for script
and style, available since version 5.3. Add that to your child theme's functions file. Scripts that plugins
print by hand may still include it until the plugin is updated.
6. Does the block editor produce valid HTML?
Core blocks generally produce valid markup, and block content is not passed through
wpautop, which removes a common source of errors. Problems come from what is put into blocks: Custom
HTML blocks with unclosed tags, headings or buttons nested inside links, content pasted from documents
into the classic block, and third-party blocks with their own markup. The block editor's message about
unexpected or invalid content concerns the block's saved format, which is a separate issue from HTML
validity.
7. Do I need a developer to fix WordPress HTML errors?
Not always. Content errors can be fixed in the editor, plugin errors often come down
to a setting or an update, and block theme templates can be adjusted in the Site Editor without code.
A developer is useful for creating a child theme, overriding PHP templates, writing filters for plugin
output and running database-wide search and replace. Whoever does it, work on a staging copy first and
keep a record of every change, so fixes survive updates.
Validate each WordPress template
The HTML Checker validates your page against W3C standards and reports errors and warnings covering
the doctype, encoding, nesting, deprecated elements, duplicate IDs, ARIA, forms and the language attribute,
so you can fix each template at its source.
Open HTML Checker Fixes โ