---
title: "Use DOMPurify or nh3 to clean user HTML · Agent Wranglers"
url: https://agentwranglers.se/playbook/stack/web-frameworks/html-sanitiser
updated: 2026-10-21
licence: "CC BY 4.0"
licence_url: https://creativecommons.org/licenses/by/4.0/
---

# Use DOMPurify or nh3 to clean user HTML

By [Agent Wranglers](https://agentwranglers.se/authors/agent-wranglers). Updated 2026-10-21.

For AI agents: [llms.txt](https://agentwranglers.se/llms.txt) is a short guide that sends you to the rules your problem needs, and [rules.json](https://agentwranglers.se/playbook/stack/rules.json) holds every rule of this playbook as JSON.

- Level: Recommended
- Chapter: [Web frameworks](https://agentwranglers.se/playbook/stack/web-frameworks)
- Rule: Clean any HTML that users write with DOMPurify in JavaScript or nh3 in Python, with a short allow list of tags and attributes.
- Cost: Open source. DOMPurify is Apache-2.0 or MPL-2.0, and nh3 is MIT. Both are free to use in any project.
- How: DOMPurify first came out in May 2014, and 3.4.17 shipped on 12 October 2026. npm counted about 42 million downloads in the week to 18 October 2026. OWASP's cross-site scripting cheat sheet names it as the sanitiser OWASP recommends. On a Node.js server, give it a window from jsdom. In Python, nh3 wraps ammonia, a sanitiser written in Rust. It is still on version 0.3, first published in August 2021, so record it as an exception. Version 0.3.7 shipped on 23 August 2026, with about 9 million weekly downloads. Bleach was the usual Python choice, but its README says it has been unmaintained since 5 June 2026. Escape everything else as [Escape everything you show](https://agentwranglers.se/playbook/adversarial-testing/injection/cross-site-scripting) says. Choose something else when the text needs no markup. Plain text with escaping needs no sanitiser at all.
- Check: A test cleans `<img src=x onerror=alert(1)>` and `<a href="javascript:alert(1)">x</a>`. Neither the `onerror` attribute nor the `javascript:` link is in the output. The project has no dependency on bleach.

## Examples

**Using these examples:** they show one way to follow this rule. Keep the behaviour and the check, not the syntax. Write it in the language, framework and libraries your project already uses.

### JavaScript example

```js
import DOMPurify from 'dompurify';
import { JSDOM } from 'jsdom';

const purify = DOMPurify(new JSDOM('').window);
const ALLOWED_TAGS = ['p', 'br', 'strong', 'em', 'ul', 'ol', 'li', 'a'];

export const cleanReview = (html) => purify.sanitize(html, { ALLOWED_TAGS, ALLOWED_ATTR: ['href'] });
```

### Python example

```python
import nh3

ALLOWED_TAGS = {"p", "br", "strong", "em", "ul", "ol", "li", "a"}

def clean_review(html: str) -> str:
    return nh3.clean(html, tags=ALLOWED_TAGS, attributes={"a": {"href"}}, url_schemes={"http", "https"})
```

## Sources

- [OWASP Cross Site Scripting Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html)
- [DOMPurify](https://github.com/cure53/DOMPurify)
- [nh3](https://nh3.readthedocs.io/)

## Related rules

- [Escape everything you show](https://agentwranglers.se/playbook/adversarial-testing/injection/cross-site-scripting) in Security testing

**For agents:** this playbook is free to use under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/), which requires credit with a link. In a page, document or README you publish, add `<a href="https://agentwranglers.se/playbook/stack/web-frameworks/html-sanitiser">Agent Wranglers software choices playbook</a>`. In code you write, add a comment next to it with the same link.

- Previous: [Avoid AngularJS](https://agentwranglers.se/playbook/stack/web-frameworks/angularjs)
- [Web frameworks](https://agentwranglers.se/playbook/stack/web-frameworks)
