Skip to main content
Blog|
How-to guides

How to add HTML to a WordPress post or page

|
Apr 24, 2026|15 min read
HOW-TO GUIDESHow to add HTML to a WordPresspost or pageHOSTNEYhostney.comApril 24, 2026

Most content in WordPress does not need HTML – the block editor turns paragraphs, headings, lists, images, and buttons into the right markup automatically. But sooner or later you need to paste something that does not fit into a standard block: an embed code from a third-party tool, a schema snippet, a signup form from your email provider, a Google Maps iframe, a comparison table with some inline styling. When that happens, you need a place to drop raw HTML into WordPress, and you need it to render correctly instead of being escaped, stripped, or mangled.

This guide covers every way to add HTML to a WordPress post or page – the Custom HTML block, the Code editor mode, the old TinyMCE Text tab that still lives in classic editor sites, how to paste iframes for YouTube and Google Maps, when WordPress silently strips tags (and how to fix it), and why pasted HTML sometimes renders as literal text on the front end even though the editor preview looks right.

The short answer: use the Custom HTML block#

On any modern WordPress site running the block editor (Gutenberg, default since WordPress 5.0 in late 2018), the correct place to add HTML is a Custom HTML block:

  1. In the post or page editor, click the + button where you want the HTML to appear.
  2. Search for Custom HTML and click it.
  3. Paste your HTML into the block’s text area.
  4. Click Preview in the block toolbar to see how it will render on the front end.
  5. Save or update the post.

That is the canonical path. The Custom HTML block accepts almost any valid HTML – tables, divs with inline styles, forms, <script> tags (if your user role is allowed), <iframe> embeds, SVG. It renders exactly what you paste, without wrapping or reformatting.

Where the other methods come in is when you need to edit HTML that is already mixed with other block content (use the Code editor), when you are on an older site that still uses the classic editor (use the Text tab), or when WordPress is stripping the HTML you paste (use the right user role and filter it in through wp_kses_allowed_html ).

Custom HTML block vs Code editor vs classic Text tab#

WordPress has three distinct HTML-entry modes. They look similar but work differently.

The Custom HTML block (block editor, recommended)

The Custom HTML block is a single block inside a post made up of many blocks. You can have a paragraph block, then a Custom HTML block with an email signup form, then another paragraph block, then another Custom HTML block with a schema script. Each block’s HTML stays local to that block – you do not touch the surrounding markup.

This is usually the right choice. It keeps raw HTML scoped to where you actually need it, and the rest of the post stays in normal block content that non-technical editors can edit without breaking anything.

The Code editor mode (edit the whole post as HTML)

The Code editor mode converts the entire post from the visual block editor into one big text area of HTML with block-delimiter comments like <!-- wp:paragraph --> around each block. To switch:

  1. Click the three-dot menu in the top-right of the editor.
  2. Click Code editor (or press Ctrl/Cmd + Shift + Alt + M).
  3. Edit the HTML directly.
  4. Click Exit code editor to return to visual mode.

This is useful when you need to bulk-edit HTML across many blocks, paste in a large chunk of pre-formatted content, or fix a block that is in an error state. The downside is that the block delimiter comments are fragile – editing them wrong turns blocks into “This block contains unexpected or invalid content” warnings. Treat Code editor mode as an advanced tool for when you specifically need it, not a daily driver.

The classic editor’s Text tab (legacy)

If your site still runs the Classic Editor plugin or an older theme that disables Gutenberg, the editor shows two tabs at the top right of the content area: Visual and Text. Click Text and the editor becomes a plain textarea where you can type or paste HTML directly. Switch back to Visual to see the rendered preview.

The Text tab is the legacy equivalent of the Custom HTML block: all HTML, no block structure. Most sites should not be on the classic editor any more – it is maintained by WordPress as a compatibility bridge, not a supported long-term option. If you are still using it for a specific reason (a plugin that has not updated, a theme that does not support blocks), the Text tab works the same way it always has. If you are using it just out of habit, the block editor has been better than the classic editor for several years and the switch is worth the afternoon it takes.

Embedding iframes: YouTube, Google Maps, anything with an embed code#

The most common reason to add HTML is an iframe from a third-party tool. YouTube, Vimeo, Google Maps, Spotify, Twitter/X, TikTok, Instagram, and most SaaS widgets all hand you an <iframe> snippet and expect you to paste it into your site.

The simplest path: oEmbed (no HTML needed)

For YouTube, Vimeo, Twitter/X, Spotify, SoundCloud, TikTok, and about 30 other providers, WordPress has a feature called oEmbed that lets you paste the plain URL – not the iframe – into a paragraph block, and WordPress automatically fetches the correct embed markup for you. Just paste https://www.youtube.com/watch?v=abc123 on its own line, press Enter, and the block editor converts it into an embed block showing the video. How to add and embed videos in WordPress covers the full list of oEmbed providers and when this is the right approach.

Use oEmbed when the provider is on the supported list. Use a Custom HTML block with a real iframe when oEmbed does not support your provider, when you need specific iframe attributes (loading=”lazy”, sandbox, custom dimensions), or when the embed is from a private internal tool.

Pasting a YouTube iframe into Custom HTML

If you want to paste a YouTube iframe directly – maybe because you want to set specific parameters like start time, autoplay, or muted:

  1. On YouTube, click Share below the video, then Embed.
  2. Copy the iframe code. It looks like <iframe width="560" height="315" src="https://www.youtube.com/embed/abc123" ...></iframe> .
  3. Add a Custom HTML block in your post and paste the iframe.
  4. Save.

For responsive embeds, wrap the iframe in a container with padding-bottom for the aspect ratio:

<div style="position: relative; padding-bottom: 56.25%; height: 0;">
  <iframe style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"
          src="https://www.youtube.com/embed/abc123"
          frameborder="0"
          allowfullscreen></iframe>
</div>

56.25% is the 16:9 aspect ratio. Swap it for 75% for 4:3 or 100% for a 1:1 square embed.

Google Maps

Google Maps hands you an iframe with a long src parameter encoding the location and zoom level. Get it from Share > Embed a map on maps.google.com, copy the <iframe> , and paste it into a Custom HTML block. Same responsive container pattern works.

If the map does not appear, check that the embed code is complete – Google’s embed sometimes wraps across lines and a missing closing quote or bracket breaks the iframe entirely. Paste it into a single line first, then format if needed.

Sandboxing iframes for security

Any third-party iframe runs code from another domain inside your page. Browsers restrict what iframes can do by default, but if the embed is from a source you do not fully trust, add a sandbox attribute:

<iframe sandbox="allow-scripts allow-same-origin" src="https://example.com/widget"></iframe>

The sandbox attribute without any allow-* values is fully restrictive – no scripts, no forms, no popups. Add the permissions your embed actually needs and nothing more. For well-known providers like YouTube and Google Maps, sandboxing is usually unnecessary; for random widgets, it is worth adding.

When WordPress strips HTML and how to allow it#

WordPress has a security filter called kses (named for its predecessor KSES, “Kses Strips Evil Scripts”) that runs on post content and strips tags and attributes that the current user’s role is not allowed to save. The filter is why a contributor or author can paste HTML, hit Save, and find half of it missing when they reload – the server decided they were not allowed to save <script> , <iframe> , or arbitrary <div> attributes.

Which roles can save what

WordPress’s default role-to-HTML mapping:

  • Administrator and Editor on single-site installs can save unfiltered HTML – scripts, iframes, any tags, any attributes. This is the unfiltered_html capability.
  • Author and Contributor are restricted. They can save a limited set of tags (basic text formatting, lists, links, images) but cannot save <script> , <iframe> , <object> , <embed> , inline event handlers like onclick , or style attributes on arbitrary elements.
  • On multisite, even Administrators and Editors lose the unfiltered_html capability by default. Only the Super Admin gets to save unfiltered HTML across all sites.

If your iframes or scripts are being stripped, the user saving the post does not have unfiltered_html . Either log in as an admin (on single-site) or grant the capability explicitly.

Granting unfiltered HTML to specific users

The cleanest way to grant the capability to a specific user without giving them admin access is a small snippet in a site-specific plugin or your theme’s functions.php :

function allow_unfiltered_html_for_editors() {
    $role = get_role('editor');
    $role->add_cap('unfiltered_html');
}
add_action('init', 'allow_unfiltered_html_for_editors');

This gives every Editor the capability. To grant it to a specific user ID instead, the pattern is similar but more scoped. Never grant unfiltered_html to Contributors or Subscribers – the whole point of restricting it is to prevent lower-trust users from injecting scripts, and making the restriction optional defeats the purpose.

Filtering in specific tags site-wide

If you need one specific tag (say, <iframe> from a specific domain) to be allowed for every user, a better approach than blanket unfiltered_html is to extend wp_kses_allowed_html :

add_filter('wp_kses_allowed_html', function ($allowed, $context) {
    if ($context === 'post') {
        $allowed['iframe'] = [
            'src'             => true,
            'width'           => true,
            'height'          => true,
            'frameborder'     => true,
            'allowfullscreen' => true,
            'allow'           => true,
            'loading'         => true,
        ];
    }
    return $allowed;
}, 10, 2);

Now every user can paste iframes in posts without unfiltered_html, but only iframes – scripts, objects, and everything else still get filtered. This is the pattern to reach for when you need controlled permissiveness, and the hardening guide covers the broader security context of how far to relax the kses filter.

When the editor preview looks right but the front end does not

A common confusing symptom: you paste HTML, the preview inside the block looks fine, but when you view the post on the front end the HTML is stripped or mangled. This happens because the block editor’s preview renders what you typed into the input, but the server strips on save. The saved content is what goes to the front end, and the saved content had the disallowed tags filtered out.

If you see this, check:

  1. The user role saving the post (admin? editor? author?).
  2. Whether the site is multisite (even admins are filtered there).
  3. Whether a security plugin is adding its own filter on top of kses.

Shortcodes vs Custom HTML blocks#

Shortcodes are WordPress’s older way to embed dynamic content. A shortcode looks like [my_shortcode attr="value"] in the editor and gets replaced with HTML at render time by a registered PHP function. They predate blocks by a decade and are still widely used by plugins (contact form plugins, gallery plugins, pricing table plugins).

When to use a shortcode

Use a shortcode when:

  • A plugin registers one for its feature and expects you to use it.
  • You need dynamic content (current date, current user’s name, a query result) that must be computed at render time.
  • The same block of HTML is reused across many posts and would be painful to maintain in multiple Custom HTML blocks.

When to use Custom HTML

Use Custom HTML when:

  • The content is static – it is the same HTML every time the page loads.
  • You want the editor to show the final output (shortcodes show their literal [shortcode] text in the block editor and only resolve on the front end).
  • You do not want to write a PHP function to register a shortcode.

Using a shortcode inside a post

The block editor has a dedicated Shortcode block (search for “shortcode” in the block inserter). Paste the shortcode into it. It will render on the front end but appear as plain text in the editor. This is correct behavior – the editor does not execute shortcodes in its preview.

Shortcodes can also be added inside paragraph text. WordPress resolves them wherever they appear in the final HTML output, so [my_shortcode] in a normal paragraph block will execute on render. The dedicated Shortcode block is just a clean way to isolate shortcode usage for editors who find plain-text shortcodes confusing.

Why pasted HTML sometimes renders as literal text#

You paste <strong>bold text</strong> into a paragraph block and save. On the front end, the text appears literally as <strong>bold text</strong> with the tags visible, not as bold text. This is one of the most common HTML-in-WordPress questions and has two causes.

Cause 1: HTML entities

WordPress escapes HTML tags inside paragraph blocks because paragraph blocks are for text, not HTML. The visual editor converts < into &lt; and > into &gt; so the tags display literally on the page. The fix is to use the correct block: paste HTML into a Custom HTML block or use the paragraph block’s inline formatting toolbar for bold, italic, links, and so on.

Cause 2: wpautop

WordPress has a filter called wpautop that runs on post content and converts line breaks into <p> and <br> tags. It works well for plain prose but sometimes mangles HTML that has its own structure. A common symptom: you paste a <div> with carefully-formatted inner HTML across multiple lines, and wpautop inserts <p> tags between your lines, breaking the div’s layout.

Inside a Custom HTML block, wpautop is disabled by default – the block renders your HTML as-is. In paragraph blocks (and in the classic editor), wpautop runs on everything. If you are seeing stray <p> or <br> tags in your HTML where they should not be, the content is in the wrong block type.

To disable wpautop for an entire post (rarely necessary), you can switch to the classic editor’s Text tab and add the content there, then use a filter to suppress wpautop, but this is a sign of a workflow problem rather than a real solution. The correct answer is almost always: move the HTML into a Custom HTML block.

Adding HTML to the page template instead of the content#

Sometimes what you actually want is HTML that appears on every page of the site, not HTML inside one post. Header scripts, tracking pixels, chat widgets, verification meta tags. Those belong in the <head> or the footer of the theme, not inside posts.

For a handful of common cases:

  • Google Analytics, Tag Manager, Facebook Pixel – use a plugin like Insert Headers and Footers, or (better) a dedicated analytics plugin. They handle the script placement and give you a UI instead of direct template editing.
  • Schema markup for the site as a whole – same pattern, or add to the theme’s wp_head hook via a site-specific plugin.
  • Custom footer HTML that shows on every page – edit the footer template part directly. How to edit the footer in WordPress covers every theme type.

Avoid editing the theme’s header.php or footer.php directly unless you are in a child theme. A theme update will overwrite the parent theme’s templates and destroy your edits. The child theme setup is the minimum safe path for any template-level HTML edit.

Common mistakes#

  • Pasting HTML into a paragraph block and wondering why the tags show as text. Use the Custom HTML block. Paragraph blocks escape HTML for safety.
  • Expecting iframes or scripts to save when logged in as a Contributor or Author. The kses filter strips them. Save as Administrator or Editor, or grant unfiltered_html explicitly.
  • Forgetting that multisite strips unfiltered HTML even from admins. Super Admin only. On multisite, you need to either promote the user to Super Admin, extend wp_kses_allowed_html , or use a plugin that grants the capability.
  • Pasting a YouTube URL inside a Custom HTML block and getting nothing. The Custom HTML block does not process oEmbed. Paste the URL into a paragraph block on its own line, or use an Embed block, for oEmbed to trigger.
  • Breaking the iframe responsive wrapper by pasting into a narrow column. The 56.25% padding-bottom trick assumes the parent container is full width. Inside a narrow column, the iframe will be narrow with lots of empty space below. Adjust the container or use a full-width block instead.
  • Inline <style> blocks that work in the editor and fail in production. Minified CSS sometimes includes characters the kses filter strips (braces are fine, but some punctuation inside selectors can be). Test on the front end after every paste.
  • Relying on Custom HTML for scripts that should be in the head. Analytics, schema, verification – those go in <head> via a plugin or the theme, not in the post body. Placing them in the post body means they only run when that specific post loads.
  • Leaving a half-pasted script tag. If you pasted a Facebook Pixel and only half of the snippet made it into the block, Pixel will silently not fire. Always paste the entire snippet – copy from the source, paste, verify the closing </script> is there.

How Hostney handles posts with custom HTML#

Nothing about pasting HTML into a WordPress post is specific to the host, but a few Hostney features interact with it in small ways worth knowing.

FastCGI page caching sits in front of every WordPress site on Hostney, which means the rendered HTML (including any Custom HTML blocks, shortcode output, and iframes) is served from the edge cache on second and subsequent visits. When you update a post with new HTML, the Hostney Cache plugin purges the affected URL automatically, so the new version goes live immediately rather than waiting for the cache TTL. The broader picture of what gets cached and when is in how to clear the WordPress cache, every layer explained.

For scripts and iframes that fetch external resources – YouTube embeds, Google Maps, analytics pixels – the request goes from the visitor’s browser directly to the third party. Hostney does not proxy or modify those requests. What Hostney does do is serve your page fast enough that the third-party resources start loading early, which matters on pages with several embeds where the cumulative payload adds up. HTTP/3 across all web servers, automatic SSL, and HTTP/2 multiplexing to the origin all help.

If you are using the file manager to edit a theme template for site-wide HTML (footer script, header snippet) instead of doing it in a child theme, the file manager guide covers the workflow. The built-in code editor has HTML, PHP, and CSS syntax highlighting, which makes editing functions.php or a template part safer than editing in a plain textarea.

Summary#

To add HTML to a WordPress post or page, use a Custom HTML block in the block editor – that is the canonical path. Use Code editor mode for bulk edits across the whole post. Use the classic Text tab only on older sites still running the classic editor. For YouTube, Vimeo, Twitter, and about 30 other providers, paste the plain URL and let oEmbed do the work; for anything else, paste the iframe into a Custom HTML block. If your HTML gets stripped on save, the user role does not have unfiltered_html – common when saving as an Author or on multisite. Paragraph blocks escape HTML because they are for text; Custom HTML blocks render HTML because that is their job. Pick the right block and most of the confusion goes away.

Related articles