roots/allow-svg: The WordPress Plugin That Treats SVG Like a Security Boundary

A zero-config Roots plugin lets WordPress accept SVG uploads by rejecting suspicious files before they ever reach the Media Library.

7 min read • View on GitHub • More from roots

A customs-style checkpoint blocks two SVG files headed toward a WordPress media library. One file passes through a clean lane, while another is stopped and stamped as unsafe. The scene explains the plugin’s core idea: SVG support works as a security gate, not a blanket allowance.
SVG is not being “enabled” here. It is being screened.
Key Takeaways

WordPress has always had an awkward relationship with SVG. The format is useful, compact, and scalable, but it is also XML. That means an SVG can carry executable behavior, not just pixels. For a CMS that has spent years avoiding native support, the problem was never “how do we let it in,” but “how do we keep it from becoming a payload?”

We’re excited to introduce Allow SVG, a minimal, security-first WordPress plugin that enables SVG file uploads with zero configuration.

Roots, Project Organization · Announcing Allow SVG | Roots

SVG uploads were never really about images

That distinction is the whole story. A PNG is a bitmap. An SVG is code-shaped content that browsers interpret. If the file contains scripts, hostile entities, or embedded foreign markup, the risk is no longer theoretical. It is the reason WordPress core has stayed cautious for years.

Most SVG plugins respond by adding more machinery: a settings screen, a sanitizer, maybe a few convenience features. `roots/allow-svg` goes the other way. It makes one decision well. If the file looks dangerous, it does not get uploaded.

Roots turns the question into a gate

The plugin’s thesis is blunt. It is not trying to repair broken SVGs or massage risky ones into safety. It sits in the upload path and acts like a checkpoint. Clean files pass. Suspicious files are rejected before they reach the Media Library.

That matters because the upload boundary is the only place where WordPress can still make a clean yes or no decision. Once a malicious file is stored and distributed, the platform is already downstream of the mistake.

A close-up view of a single SVG document under a magnifying glass. Hidden inside the file are small dangerous fragments such as script tags, entity declarations, foreignObject nesting, and event-handler attributes. The image explains that the validator is looking for attack mechanisms inside the XML, not just obvious bad file names.
The validator is reading for mechanisms, not appearances.

What the validator is actually looking for

The interesting part is not just that the plugin checks for bad SVG. It checks for classes of abuse that matter in XML. Script tags are obvious. Inline event handlers are obvious. But the list also reaches into `DOCTYPE`, `ENTITY`, `foreignObject`, namespaced variants, and obfuscation tricks that try to hide hostile behavior in plain sight.

That makes the implementation feel more like a threat model than a convenience layer. The file is not being beautified or cleaned. It is being interrogated.

The upload path is short on purpose. The file is either clean enough to pass or it is turned away at the door.

That design keeps the plugin auditable. There is no hidden remediation step, no background rewrite, no second system deciding what “safe enough” means after the fact. The trade-off is clear too: a deny-first system can reject some legitimate files if they resemble hostile ones. Roots is choosing certainty over convenience.

Why deny-first beats sanitize-first

ApproachConfigSecurity postureDependenciesDeveloper experienceRisk of false positives
Deny-first validationNoneRejects suspicious files before uploadZero production dependenciesVery simpleModerate
Sanitize-first filteringOften a settings pageAttempts to clean files after uploadUsually heavier librariesMore flexibleLower, but with hidden transforms
Block SVG entirelyNoneEliminates SVG risk by forbidding the formatNoneSafest but least usefulNone

This is why the comparison is philosophical as much as technical. Sanitizing says, “I can make this usable.” Deny-first validation says, “I am not going to guess.” For a file format that can carry executable structure, that is a strong and defensible boundary.

The test suite is the real product

The fixtures tell you what the maintainers fear. Malicious XXE samples. Entity bombs. ForeignObject payloads. Corrupted XML. That test corpus is more than QA. It is a documented attack surface.

A WordPress plugin that enables SVG uploads with validation to block malicious files.

retlehs, Key Contributor · roots/allow-svg README

That matters because security plugins are often vague about their coverage. Here, the threat model is visible in the repository itself. The tests are not just proving that SVG uploads work. They are proving which failures must never slip through.

How it compares to the rest of the SVG plugin world

Plugin stancePrimary moveTrade-offBest for
roots/allow-svgValidate and reject suspicious filesLess forgiving, fewer featuresTeams that want a narrow security gate
Sanitize-first SVG pluginsStrip dangerous content after uploadMore moving parts and hidden transformsSites that want convenience and broader compatibility
Feature-heavy SVG pluginsAdd UI and extra rendering optionsMore surface area than the upload problem really needsUsers who value configuration over minimalism
No SVG supportDisable the format entirelyMaximum safety, minimum usefulnessOrganizations that can live without SVG

`allow-svg` does not try to win every category. It wins the one that matters to its audience: trustability. The plugin is small enough to inspect, strict enough to matter, and opinionated enough to avoid pretending that every SVG can be made safe.

A modern PHP plugin in a legacy ecosystem

The other quiet story here is implementation discipline. This is a WordPress plugin, but it reads like a modern PHP project. Strict types, PHP 8.2, GitHub Actions, Playwright, and no production dependencies all suggest the same thing: the team is optimizing for maintainability, not breadth.

That is a useful reminder. In older ecosystems, modern engineering practice can be the differentiator. Security is not only about the validator logic. It is about how easy the code is to reason about, test, and keep honest over time.

Sources: roots/allow-svg, Roots announcement, Roots discourse thread. For a close read on the implementation and tests, the repository itself is the best source.