AntiXSS: a PHP sanitizer that turns attacks into harmless markup
㊙️ AntiXSS | Protection against Cross-site scripting (XSS) via PHP
At a glance
- What is it?
- voku/anti-xss is a long-running PHP library that cleans untrusted HTML by rewriting it rather than by stripping it, and it is used across thousands of PHP projects through Packagist. It handles the obfuscations that trip naive filters, but it is a sanitizer for one layer of output, not a replacement for output encoding or a content security policy.
- Who is it for?
- Adopt AntiXSS when you accept HTML from users and need to keep its structure rather than strip it, and when your stack is PHP with a Composer workflow already in place. Do not adopt it as your only defence, because it operates on one layer of output, and the project's own notes point to output escaping, a content security policy and a dedicated sanitizer for configurable cases.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 10 days ago.
- What is it written in?
- Mainly PHP, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A sanitizer that rewrites rather than deletes
AntiXSS is a PHP library for protection against cross-site scripting. The distinguishing approach is visible in the very first example in the documentation. A string containing a script element does not have the tag removed; the tag is gone but its text content is kept and its punctuation is encoded, producing output where the alert text survives as visible characters rather than as executable markup.
That is a different philosophy from a filter that either allows input through or rejects it. The library is meant for the case where a user submits rich HTML, for instance a comment body or an article, and the application wants to store and later display that HTML without letting the user run code in a viewer's browser. The output is therefore valid-looking markup with the dangerous parts neutralised, not a boolean answer to whether the input was acceptable.
The project is a mature one in the ordinary sense of the word. It is on Packagist, the latest stable version listed in the badges is 4.1.45 from 2026-10-08, the last push to the repository was on 2026-10-12, the licence is MIT, and the documentation lives at its own docs site. The default branch is master and the implementation sits in src/ with the helper class under voku/helper.
The library is not a framework and does not try to be. It is a single class you instantiate and call, and the documentation is a list of what it does to specific payloads.
The obfuscations the examples walk through
The examples are the documentation, and they are ordered by the trick used to hide the payload. The first is plain HTML, with a script element. The second is hexadecimal entities, an IMG tag whose attributes are written as numeric character references, which is cleaned down to an IMG element with no attributes. The third is a unicode hex character inside a javascript URL, where an anchor with a href of that kind comes back as an anchor with the href gone. The fourth is a unicode control character sequence embedded in the scheme name, which produces the same result.
The fifth case covers CSS. A list item whose style attribute contains a url with a javascript scheme is cleaned to a list item with no style attribute at all. The example that follows shows the escape hatch: the same input, with style removed from the list of removed attributes first, comes back as a list item that still has a style attribute, but the javascript scheme inside it has been neutralised to alert(0). The distinction matters, because the two configurations produce very different HTML for a rich text editor.
The sixth example shows detection rather than cleaning. The same class exposes an isXssFound() method, and after cleaning a payload that contained a script tag it returns true. The seventh shows tag level configuration: with iframe removed from the list of evil HTML tags, an iframe with an onclick attribute is kept, the onclick attribute stripped, and the rest of the embed URL preserved. The class exposes removeEvilHtmlTags and removeEvilAttributes for exactly this kind of per-application tuning, and the fact that these lists are public API is the most important thing to understand about the library.
Installing through Composer and constructing the helper
The library is a Composer package, and installation is a single command:
composer require voku/anti-xssUsing it means instantiating the helper class, which requires the Composer autoloader to be loaded first:
use voku\helper\AntiXSS;
require_once __DIR__ . '/vendor/autoload.php'; // example path
$antiXss = new AntiXSS();One dependency detail is worth knowing before you install. The package depends on voku/portable-utf8, which sets default_charset to UTF-8 through ini_set() while the autoloader loads. If an application needs to prevent that, the documentation gives the workaround, a constant defined before the autoloader is required:
define('PORTABLE_UTF8__DISABLE_AUTO_ENCODING', true);
require_once __DIR__ . '/vendor/autoload.php';The ordering is the whole point of that snippet. A constant defined after the autoloader has run has no effect, since the ini_set call has already happened by then. For an application that has its own charset policy, or that runs on a shared host with a php.ini it does not control, this is a small but real footgun, and it is documented rather than hidden.
The documentation tells you not to rely on it alone
The first section of the notes is the most important part of the whole document, and it is a list of what to do instead of, or alongside, this library. The first note is to use filter_input() rather than reading the global superglobals such as $_SESSION, $_GET, $_POST and $_SERVER directly. The second is to use html-sanitizer or HTML Purifier when a more configurable solution is needed. The third is to add a Content Security Policy. The fourth, in capitals, is not to write your own regex to parse HTML. The fifth is to read the OWASP XSS prevention cheat sheet, and the sixth is to test the result with the Zed Attack Proxy.
Read as a whole, the notes say that sanitizing one output is not the same as securing an application. Input handling, output encoding, a policy header and testing are separate jobs, and AntiXSS covers one of them.
The fourth note deserves extra weight for anyone tempted to extend the library. The sanitizing logic works on dictionaries of patterns and rules, and the repository layout shows the machinery in src/ with the test suite in tests/. The documentation also shows a prompt intended for language models to help expand regression coverage around those dictionaries, which tells you something about how the rules are maintained: they are a large, hand-curated set, and the project treats coverage of them as a continuous task rather than a finished one.
Mutation testing and static analysis in the same loop
The test setup is more serious than the average library's. Composer is a prerequisite, and the suite is run from the repository root:
XDEBUG_MODE=coverage ./vendor/bin/phpunit -c phpunit.xmlOn top of that, CI runs Infection with PHPStan integration on the PHP 8.3 pull request job. The configuration is infection.json5.dist, the requirement is 100 percent MSI on the mutated diff, and any mutant that times out fails the run, with the stated reason that sanitizer loops must not silently regress. That last point is the interesting one. A sanitizer is a piece of code that runs on every request with untrusted input, and a mutation that turns a loop into an infinite or pathological one would be a denial of service rather than a wrong answer, which is why timeouts are treated as failures.
The same toolchain can be run locally, and the documentation gives the exact commands for that, starting with allowing the Infection plugin:
composer config --no-plugins allow-plugins.infection/extension-installer true
composer require --dev phpstan/phpstan:^2.1 infection/infection:^0.32.7 --no-update
composer update
XDEBUG_MODE=coverage ./vendor/bin/infection --configuration=infection.json5.distThe repository layout backs this up with phpstan.neon, phpcs.php_cs, phpunit.xml and phpunit9.xml, plus build/ and tools/ directories, an .oss-scanner directory and configuration for several review services. The build/ directory is what the badge generation in the README refers to, since the readme itself is marked as auto-generated by a helper script from a base file.
Where AntiXSS is the wrong tool
The clearest case against it is that it does not cover the layers where most XSS actually lands. A value inserted into a JavaScript context, into a URL parameter, into a CSS file or into an attribute that the library does not know about will not be touched by xss_clean, because the library is working on HTML fragments. An application that reflects user input into a script tag or an event handler attribute without encoding it remains vulnerable no matter how well the HTML body is sanitized. The project's own notes point at this, which is why output escaping and a content security policy are on the list rather than in a footnote.
The second limitation is the default configuration versus real applications. Out of the box the library removes inline style attributes and treats iframe as an evil tag, and the examples show how to change both. Every such decision is an application decision that the library cannot make for you, and each one is a place where a mistake is easy: allow style attributes and you re-admit a CSS based vector, allow iframes and you have to trust every URL in them, because the library keeps the src value of the embed it is shown.
The third is that a rewrite-based sanitizer has a floor on how it handles input it does not recognise. The examples cover hexadecimal entities, unicode control characters and CSS URLs because those are the documented cases. Anything the dictionaries do not describe passes through the same pipeline, and the project cannot enumerate every encoding a browser will accept. That is an argument for pairing it with a content security policy and real testing, not an argument against the library.
Editorial conclusion
Adopt AntiXSS when you accept HTML from users and need to keep its structure rather than strip it, and when your stack is PHP with a Composer workflow already in place. Do not adopt it as your only defence, because it operates on one layer of output, and the project's own notes point to output escaping, a content security policy and a dedicated sanitizer for configurable cases. Before you rely on it, read the six notes at the top of the documentation, decide explicitly whether to allow inline style attributes and iframe tags through removeEvilAttributes and removeEvilHtmlTags, and check whether the portable-utf8 dependency's automatic charset setting conflicts with your application.
Frequently asked questions
What does voku/anti-xss do?
It is a PHP library for protection against cross-site scripting that sanitizes untrusted HTML by rewriting it, keeping the text content while neutralising script tags, event handler attributes and javascript URLs. Its most recent listed version is 4.1.45 from 2026-10-08, and it is distributed on Packagist under the MIT licence.
How do I install and use AntiXSS?
Install it with composer require voku/anti-xss, load the Composer autoloader, and instantiate the helper class from voku\helper\AntiXSS. The class then exposes xss_clean, isXssFound, removeEvilHtmlTags and removeEvilAttributes for adjusting which tags and attributes are stripped.
Is AntiXSS enough to prevent XSS on its own?
The project's own notes say to use filter_input() instead of the global superglobals, to add a Content Security Policy, to use html-sanitizer or HTML Purifier when a more configurable solution is needed, and to test with the Zed Attack Proxy. AntiXSS covers sanitizing HTML output, not encoding in other contexts.
How do I keep inline styles or iframe tags in AntiXSS?
You remove them from the removal lists before cleaning. The documentation shows removeEvilAttributes with an array containing style to allow style attributes, and removeEvilHtmlTags with an array containing iframe to keep iframe elements, in both cases before calling xss_clean.
What is the portable-utf8 issue with AntiXSS?
The package depends on voku/portable-utf8, which sets default_charset to UTF-8 via ini_set() while the autoloader loads. To prevent that, define the constant PORTABLE_UTF8__DISABLE_AUTO_ENCODING before requiring the Composer autoloader, since a later definition has no effect.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/voku-anti-xss)Was this useful?