You paste a block of HTML into WordPress, and it doesn’t come out the way you wrote it. An embed disappears after saving, extra <p> and <br> tags appear from nowhere, the editor says the block “contains unexpected or invalid content”, or a script that works everywhere else does nothing on your page.
These problems look random, but each one has a specific cause inside WordPress. This guide shows how to tell which one you’re dealing with and how to fix it properly.
First, Work Out What “Broken” Means
Match what you see to one of these, then jump to that section:
- Tags like
<iframe>,<script>or<style>vanish after saving: WordPress is filtering HTML for your user role. - The editor shows “This block contains unexpected or invalid content”: the block’s saved markup no longer matches what the block expects.
- Extra
<p>or<br>tags break your layout: automatic paragraph formatting is running on your HTML. - The HTML is intact, but a script doesn’t run: usually a caching or optimization plugin, or a script that runs before the page is ready.
- The HTML is intact, but it looks wrong: your theme’s CSS is overriding it.
To see what WordPress actually stored, open the post, click the three-dot menu at the top right, and switch to Code editor. If your HTML is different there, WordPress changed it when saving. If it’s the same, the problem happens when the page is displayed.
Tags Disappear After Saving: Your Role Can’t Post Unfiltered HTML
WordPress filters post content when it’s saved, using an allowlist of safe tags and attributes. Users who have the unfiltered_html capability skip that filter. Everyone else has anything outside the allowlist removed, including <script>, <iframe>, <style>, <form> and inline event handlers like onclick.
By default, only these users have unfiltered_html:
- On a single site: Administrators and Editors.
- On a multisite network: only Super Admins. Site administrators are filtered too.
So when an Author or Contributor pastes an embed code into a Custom HTML block, the editor shows it fine, but the tags are stripped as soon as the post is saved.
Ways to fix it, safest first:
- Use an embed block instead. For YouTube, Vimeo, X, Spotify and many other services, paste the plain URL on its own line, or use the matching Embed block. WordPress builds the iframe itself, and no user needs unfiltered HTML.
- Have an Editor or Administrator add the code. For a one-off snippet, this is usually the simplest answer.
- Move the code into a shortcode or custom block in your theme or a small plugin, so authors insert
[my_widget]rather than raw HTML.
Avoid giving unfiltered_html to roles that don’t need it. Anyone who can post a <script> tag can run JavaScript in the browser of every visitor and every logged-in administrator who views that page.
If even Administrators lose their HTML, check wp-config.php for this line, which removes the capability from everyone:
define( 'DISALLOW_UNFILTERED_HTML', true );
Some security plugins and managed hosts set it on purpose. Remove it only if you understand why it was added.
“This Block Contains Unexpected or Invalid Content”
Each block saves its content as HTML with comment markers around it. When the editor loads the post, it checks that the saved HTML matches what the block would produce. If they don’t match, you get this warning instead of the block.
The usual causes:
- The HTML inside a block (not a Custom HTML block) was edited by hand in the Code editor.
- Content was copied in from another site, an email, or a page builder.
- A plugin that registered a block was updated or removed, and its saved markup changed.
- A search-and-replace on the database changed markup inside block comments.
Use the options the editor offers on the block:
- Attempt recovery rebuilds the block from its current content. Check the result, because it can drop changes the block doesn’t support.
- Convert to HTML turns it into a Custom HTML block and keeps your markup exactly as it is. This is the right choice when the custom markup is what you want.
- Convert to Classic Block puts the content in the classic editor, which brings back automatic paragraphs (see the next section).
To prevent it, put any HTML you write yourself in a Custom HTML block instead of editing other blocks’ markup. The Custom HTML block saves whatever you type and doesn’t validate it against a template.
Extra <p> and <br> Tags: Automatic Paragraphs
WordPress has a function called wpautop that turns line breaks into <p> and <br> tags. It runs on content from the classic editor and the Classic block. Content made of blocks skips it, which is why the same HTML can behave differently in two posts.
If your markup is getting wrapped in stray paragraphs:
- Move it into a Custom HTML block in the block editor.
- In the classic editor, use the Text tab and remove blank lines inside your HTML, since each blank line becomes a paragraph break.
- If a shortcode’s output gets wrapped in
<p>tags, put the shortcode on its own line, and make sure the shortcode function returns its HTML instead of echoing it.
You may find snippets online that switch off wpautop for the whole site. That fixes one post and changes the formatting of every older classic post, so only do it if you’ve checked those posts first.
The HTML Is Fine but the Script Doesn’t Run
If the Code editor shows your <script> exactly as you wrote it, WordPress didn’t strip it. Check these instead:
- Optimization and caching plugins. Features like “delay JavaScript”, “defer”, or “combine JS” in LiteSpeed Cache, WP Rocket and similar plugins can stop inline scripts from running, or run them before the library they need. Exclude the script, or test with the feature turned off. Purge the cache after each change.
- Timing. A script placed above the element it uses runs before that element exists. Move it below the element, or wrap it in a
DOMContentLoadedlistener. - The editor preview. Scripts in a Custom HTML block generally don’t run inside the editor. Test on the published page or a preview.
- Security headers. A Content Security Policy set by your host or a security plugin can block inline scripts. The browser console (F12) shows a CSP message if that’s the cause.
For code that should load on many pages, like analytics or chat widgets, don’t paste it into posts at all. Add it once with wp_enqueue_script() in your theme or a small plugin, or with your theme’s header/footer code setting.
The HTML Is Fine but It Looks Wrong
If the markup is intact and nothing is blocked, your theme’s CSS is styling it. Right-click the element and choose Inspect to see which CSS rules apply. Give your HTML its own class and add styles for that class under Appearance → Customize → Additional CSS (or in the Site Editor’s Styles panel on block themes), rather than using inline style attributes everywhere.
Still Broken? Rule Out Plugins and the Theme
If none of the above explains it, a plugin may be filtering content on save or on display. Test on a staging copy: switch to a default theme like Twenty Twenty-Five, deactivate plugins, and re-enable them one at a time until the problem returns. Our guide to diagnosing WordPress plugin conflicts walks through doing that without taking your live site down.
Summary
Check the Code editor first to see whether WordPress changed your HTML on save. Tags that disappear mean your role lacks unfiltered_html. The invalid content warning means a block’s markup was edited, so use Custom HTML blocks for your own code. Stray paragraphs come from wpautop in classic content. If the HTML is intact, look at caching plugins, script timing, security headers, and theme CSS.