Yes, but the honest answer has three layers to it: where you paste it determines whether it renders as intended, who’s logged in determines whether WordPress strips it down before saving, and what the code actually does determines whether pasting it is a good idea at all. A lot of confusion around this comes from people hitting one of those three layers unexpectedly, pasting a tracking script that vanishes on save, or a working snippet that breaks the moment a lower-privilege editor tries to reuse it, or a perfectly valid embed that Gutenberg refuses to render cleanly. Each of those has a specific, learnable cause.
The role-based filtering layer most people never notice
WordPress ties the ability to save raw, unfiltered HTML, including <script> tags, <iframe> elements, and other structurally powerful markup, to a specific capability called unfiltered_html. By default, Administrators and Editors on a standard single-site install have this capability; Authors, Contributors, and Subscribers don’t. If you’re logged in as an Administrator, pasting a script tag into a Custom HTML block or the classic editor’s text mode saves exactly what you typed. Log in as an Author and paste the identical code, and WordPress’s wp_kses sanitization function silently strips out the tags it doesn’t consider safe for that role, saving a stripped-down version with no warning that anything was removed. This is one of the most common sources of “it worked when I tested it but broke when my client tried it,” because the difference in behavior is entirely dependent on which account did the pasting, not on anything about the code itself. On multisite installs, this gets stricter still by default, even Administrators of individual sites within a network don’t have unfiltered_html unless a network administrator explicitly grants it, specifically because a compromised or careless site admin within a larger network is a much bigger liability when they can inject arbitrary script across a shared environment.
Where you paste it changes what actually happens
The Custom HTML block (Gutenberg). This is the most direct route and, for a user with unfiltered_html, the most reliable one, content in this block passes through with minimal interference from the editor’s own sanitization on save (though it may still get filtered depending on role, per the section above). It’s the right choice for embedding a widget, a form, or a small script that needs to live inline with your content.
A regular paragraph or text block. Gutenberg’s standard blocks aren’t designed to hold raw markup and will typically escape it, rendering your HTML tags as visible text on the page rather than interpreting them, which is usually not what anyone intended. If you paste a chunk of HTML into a normal text block expecting it to render, and instead see the literal angle brackets and tag names printed on the page, this is why.
Widgets. The dedicated Custom HTML widget behaves similarly to the block editor equivalent, respecting the same capability-based filtering, and is the standard place for sidebar or footer embeds that need to appear across many pages without duplicating the code into every post.
Theme Customizer → Additional CSS. This field is specifically scoped to CSS, not general HTML, and won’t render arbitrary markup at all, a common mistake is trying to drop a tracking script here because it “seems like the right settings area,” when it’s actually a dedicated stylesheet field with no HTML execution context.
A page builder’s HTML/Code element. Elementor, WPBakery, Beaver Builder, and similar tools each ship their own dedicated HTML element, generally more permissive than Gutenberg’s default filtering since these plugins often assume the person using them already has the access level needed, but behavior still varies by plugin and by the WordPress role filtering underneath it.
The security reasoning behind all this friction
It’s worth understanding why WordPress makes this deliberately inconvenient rather than just letting anyone paste anything anywhere, because the friction is protecting against a real, well-documented attack class. If a lower-privileged user, a guest author, a contributor, someone whose account gets compromised through a weak password, could freely inject arbitrary <script> tags into published content, that’s a direct path to stored cross-site scripting: malicious JavaScript that executes in the browser of every visitor who loads that page, including any administrator who happens to view it while logged in, potentially letting the attacker hijack that admin’s session and escalate to full site control. This is exactly the mechanism unfiltered_html and wp_kses exist to prevent, and it’s a genuinely important distinction from a purely “arbitrary annoyance” framing, the restriction on lower roles isn’t WordPress being needlessly cautious, it’s closing a specific, historically exploited privilege-escalation path.
What to actually check before pasting third-party code
Even with full unfiltered_html access, pasting code you didn’t write and don’t fully understand carries real risk, and it’s worth a short mental checklist before doing it. Where did the snippet come from, the vendor’s own official documentation, or a copy-pasted forum answer of uncertain provenance? Does it load an external script from a third-party domain, and if so, is that domain one you recognize and trust, since a compromised or malicious third-party script has full access to your page’s DOM and any cookies accessible to JavaScript on that domain? Does the snippet include an inline event handler or a dynamic code-execution pattern that’s harder to audit at a glance than a clean, self-contained function? None of this means avoid third-party embeds entirely, most sites legitimately need analytics, chat widgets, payment buttons, and social embeds, but it does mean treating a pasted script with the same scrutiny you’d apply to installing a plugin, because functionally it has similar reach into your site.
A safer alternative for anything beyond a one-off embed
If you’re pasting the same snippet across many posts, or embedding something that needs to survive theme changes, hardcoding it directly into post content is fragile, a code snippet manager plugin (rather than editing functions.php directly, which risks a fatal error taking the whole site down on a typo) or a proper header/footer script injection plugin gives you one place to manage and update the code, with the ability to disable it cleanly if something breaks, rather than hunting through dozens of individual posts to find every copy. This is also the more maintainable pattern for anything genuinely reusable, like a consistent tracking pixel or a chat widget meant to appear site-wide, versus a one-off embed genuinely specific to a single post’s content.
Content Security Policy: the layer that can silently block a working embed
If your site (or a security plugin, or your host, or a CDN like Cloudflare) has a Content Security Policy header configured, it can restrict which domains scripts, iframes, or styles are allowed to load from, independent of whether WordPress itself saved your pasted code correctly. A snippet that saves fine and looks correct in the page source can still fail to actually execute if CSP blocks the specific external domain it’s trying to load from, and the failure typically shows up only in the browser’s console as a CSP violation, not as any visible error on the page itself. If a pasted embed looks right in the HTML but simply doesn’t do anything, checking the browser console for CSP violation messages is a faster diagnostic step than re-checking the code itself, which is often perfectly fine.
Comments and forms: the other place pasted HTML shows up
Everything above focuses on post and page content, but the same underlying filtering logic extends to comments and any front-end-submitted content, with an important difference: WordPress core strips essentially all HTML from public comment submissions by default via wp_filter_kses(), allowing only a small safelist of basic formatting tags (bold, italic, links with specific attributes) regardless of who’s commenting, because a public comment form is an unauthenticated or low-trust input surface by definition and needs far tighter filtering than an editor’s post content field. If you’re building a community site with BuddyPress or a similar front-end publishing feature and want members to be able to embed richer content in their posts or activity updates, that capability needs to be deliberately and carefully extended, typically through a filter on wp_kses_allowed_html scoped narrowly to the specific context, rather than assumed to work the same way the admin editor does. Extending raw HTML privileges to public-facing user-generated content without careful scoping is one of the more common ways a site accidentally reopens the exact vulnerability the default restrictions exist to close.
Testing before it goes live
Because pasted HTML, especially anything involving scripts or iframes, can affect page layout, break other elements, or in rare cases even take down page rendering entirely if malformed, testing on a staging copy or at minimum previewing as a draft before publishing to a live, indexed page is worth the extra step, particularly for anything sourced from outside your own team. A staging environment also lets you safely test with an account at each relevant permission level if you’re building something a lower-privileged editor will need to reuse later, so you catch the filtering behavior described earlier before it surprises someone in production. It’s also the right place to test across a couple of different browsers if the embed relies on any modern JavaScript feature, since a script that works flawlessly in the browser you happen to be editing in can still fail silently for a visitor on an older or less common browser your third-party vendor didn’t test against.
oEmbed versus raw HTML: two different mechanisms that look similar
When you paste a bare URL from YouTube, Twitter/X, Vimeo, or another oEmbed-supported service on its own line in the block editor, WordPress doesn’t treat it as HTML at all, it recognizes the URL pattern and automatically converts it into an embed block, fetching the provider’s official embed markup via the oEmbed protocol and rendering that instead. This is a separate, safer mechanism from pasting raw iframe or script markup yourself, because the provider controls exactly what gets embedded and WordPress core maintains an allowlist of trusted oEmbed providers rather than accepting arbitrary embed code from anywhere. It’s worth knowing the difference because sometimes the simplest, most reliable way to “paste HTML” for a supported service isn’t to paste HTML at all, it’s to paste the plain URL and let WordPress’s oEmbed system handle it, which also tends to produce cleaner, more consistently responsive output than a hand-copied iframe snippet from the provider’s raw embed code generator.
The Gutenberg validation error that confuses almost everyone the first time
If you paste HTML into a block that wasn’t designed for it, or if a Custom HTML block’s content gets modified in a way Gutenberg doesn’t expect between saves, you can hit a message reading something like “this block contains unexpected or invalid content” with options to attempt a fix, convert to blocks, or keep as HTML. This happens because Gutenberg stores a content hash comment alongside certain block types and re-validates the block’s markup against that hash on load; if the underlying HTML changed outside the expected editing flow, through a plugin filter, a database edit, or manually modifying the raw content some other way, the validation fails even though the content itself may render just fine on the actual front end. The safest resolution in almost every case is choosing “Keep as HTML,” which preserves your pasted content exactly as-is without WordPress attempting to reformat it into a different block structure that might mangle the original markup.
A concrete failure mode: the unclosed tag that breaks the rest of the page
Raw HTML pasted without matching, correctly closed tags can cause real layout damage beyond just the embed itself. An unclosed <div> from a hastily copied chat-widget snippet, for instance, can cause every element that follows it in the DOM to nest inside that unclosed tag, which might not be visually obvious immediately but can break floated layouts, cause unexpected width or padding inheritance, or interfere with a theme’s own CSS selectors that assumed a particular DOM structure. This is a common, frustrating bug because it often shows up as “my footer looks wrong” or “my sidebar shifted,” with no obvious connection back to a chat widget pasted three sections earlier in a completely different part of the page. Running the page through the W3C Markup Validator, or simply opening browser DevTools and inspecting where the DOM structure actually nests relative to what you expect, is the fastest way to catch this class of problem, and it’s a habit worth building specifically for any snippet copied from a third-party dashboard rather than written by hand, since generated code varies enormously in how carefully it closes its own tags.
Fixed-width embeds and the mobile problem
A lot of embed code generated by third-party tools, particularly older widget generators, ships with hardcoded pixel width and height attributes on an iframe rather than responsive, percentage-based sizing. Pasted as-is, this produces an embed that looks fine on the desktop screen the code was copied while looking at, then either overflows the container or gets awkwardly cropped on a narrower mobile viewport, since the iframe never adapts to its actual available space. The fix is either wrapping the iframe in a responsive container using the well-established padding-based aspect-ratio trick (or the newer CSS aspect-ratio property directly), or checking whether the provider offers a “responsive” embed code variant, which most major services do today even if their default generator still produces the older, fixed-size markup by default.
The REST API respects the same rules
If content, including raw HTML, gets created or updated through the WordPress REST API rather than the block editor directly, a common pattern for headless setups, automated publishing tools, or integrations, the same capability-based filtering still applies based on which authenticated user or application password the API request is acting as. A REST API request authenticated as a user without unfiltered_html will have its HTML sanitized on the way in exactly the same way a manual paste through the editor would, which is worth knowing if you’re troubleshooting why an automated pipeline is stripping tags that work fine when a site administrator pastes the same content manually through wp-admin.
Putting it together
Yes, you can copy and paste HTML into WordPress, and for most legitimate use cases, embedding a widget, a form, an analytics snippet, a payment button, it’s a completely normal, supported workflow through the Custom HTML block or widget. The friction you might run into almost always traces back to one of three things: the account you’re logged in as lacking unfiltered_html, pasting into a field that was never meant to hold raw markup in the first place, or a security layer like CSP quietly blocking execution after the fact. Understanding which of those three is actually happening turns a frustrating “it just doesn’t work” into a two-minute fix rather than an open-ended debugging session.
The broader pattern worth internalizing is that WordPress’s editor isn’t being arbitrarily restrictive when it filters or rejects markup, it’s applying the same permission model that governs everything else on the site, plugin installation, theme editing, user management, to the specific case of raw code embedded in content. Once that clicks, the behavior stops feeling inconsistent and starts feeling like exactly what you’d expect: an administrator gets full trust because the platform assumes they understand the risk, a contributor doesn’t because the platform can’t assume that, and every embed you paste is, functionally, a small piece of code running with whatever trust level your account carries at the moment you save it.
Related reading:
Best Code Snippet Manager: 6 Tools Compared (2026)