Server
Server sanitizing
Clean posted HTML on your server with @viveeditor/sanitize for Node or viveeditor/sanitizer for PHP 8.4+, on save and on print.
The editor only produces a known set of HTML, but anyone can post any HTML to your server. Clean it there before you store or show it. @viveeditor/sanitize for Node and viveeditor/sanitizer for PHP 8.4+ keep exactly what the editor produces and remove everything else. They share one allowlist and one test corpus and give the same output.
Install
For Node.js 20.19 or newer:
npm install @viveeditor/sanitize
For PHP 8.4 or newer, with the dom and mbstring extensions and no other dependencies:
composer require viveeditor/sanitizer
On Laravel, use viveeditor/laravel, which brings the PHP sanitizer with it.
Clean the HTML
import { sanitizeHtml } from '@viveeditor/sanitize'
const clean = sanitizeHtml(request.body.html, { embeds: true })
use Viveeditor\Sanitizer\Sanitizer;
$clean = Sanitizer::sanitize($html, ['embeds' => true]);
Every element and attribute is rebuilt with a full HTML5 parser (parse5 in Node, Dom\HTMLDocument in PHP), so the output reads the same way in every browser. Cleaning twice gives the same result.
Options
| Option | Default | Effect |
|---|---|---|
embeds |
false |
Keeps video and audio embeds. Players are rebuilt from the provider and id, never copied. |
allowBase64 |
false |
Allows images stored as data: addresses (PNG, JPEG, GIF, WebP, AVIF). |
allowRelativeImages |
false |
Allows relative image addresses. |
maxBytes |
2 MB | HTML larger than this throws TOO_LARGE. |
Turn allowRelativeImages on only if you need it: a relative image address makes every reader’s browser send a GET request to your own site, which a writer could aim at an address that changes something.
Errors
The sanitizer never cuts HTML short. When it cannot clean the input, it throws:
| Code | When |
|---|---|
TOO_LARGE |
The HTML is larger than maxBytes. |
TOO_COMPLEX |
The structure would make the parser slow or memory hungry: more than 64 attributes on one tag, nesting deeper than 512 levels, or far more elements than the input has tags. The editor never writes such HTML. |
In Node the error is a SanitizeError with a code:
import { SanitizeError, sanitizeHtml } from '@viveeditor/sanitize'
export function cleanArticle(html: string): string | null {
try {
return sanitizeHtml(html)
} catch (error) {
if (error instanceof SanitizeError) return null
throw error
}
}
In PHP it is a SanitizeException whose reason is SanitizeException::TOO_LARGE or SanitizeException::TOO_COMPLEX:
use Viveeditor\Sanitizer\SanitizeException;
use Viveeditor\Sanitizer\Sanitizer;
try {
$clean = Sanitizer::sanitize($html);
} catch (SanitizeException $error) {
$clean = null;
$reason = $error->reason;
}
What is kept
- Links keep
http,https,mailto,teland relative addresses, and always getrel="noopener noreferrer nofollow". - Images need
httpsaddresses.allowBase64andallowRelativeImageswiden that. - Embeds are removed unless you pass
embeds: true. stylekeeps only text alignment, text and highlight colour, and table widths.id,name,formand event handlers never pass.- Scripts, styles, frames, SVG and similar elements are removed together with their content.
- Mentions, formulas and footnotes are kept in exactly the form the editor writes them. A mention id or formula that fails its check, or one placed inside a link or a footnote, is reduced to its text. Footnotes keep only bold, italic and links.
PHP memory note
In PHP the parser cannot be stopped from inside, so some short hostile inputs (a few tens of KB of unclosed formatting tags) can reach PHP’s memory_limit before the check runs. PHP then ends that request with a fatal error, which is safe but not a clean exception.
Keep memory_limit and max_execution_time at their usual values for the endpoints that sanitize, and limit the request body size there.
When to clean
- On save. Clean the posted HTML before you store it.
- On print. Show stored HTML through a cleaning helper too (
@viveHtmlin Laravel). Rows written before you added cleaning, or written outside your forms, then stay safe.
Never print raw HTML
Do not print stored HTML unescaped without cleaning it, even if it was cleaned once on save.
Related pages
The editor already produces clean HTML. Why clean it again on the server?
Because anyone can post any HTML to your server without using the editor. The browser is not a security boundary.
Do the Node and PHP sanitizers give the same result?
Yes. They share one allowlist and one test corpus and give the same output.
What happens when the HTML is too large?
The sanitizer throws a TOO_LARGE error instead of cutting the HTML. The default limit is 2 MB.