NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
PyPI
PyPI, #5657 most downloaded on PyPI
HTML sanitizer
Latest stable
2.7.0
Last release 20 days ago
25 Oct 2026
Release timing varies
gaps range from 1 weeks to 1.5 years
Some releases are documented
notes for 18 of 38 stable releases
Nothing withdrawn
no release was ever pulled
10 years old
38 releases · first in 2017
Drag across the ruler to compare, or tap a release to open it.
Added the preserve_whitespace setting which keeps whitespace inside the listed tags as-is. Defaults to {"pre"}, which only has an effect if pre is add
Added the preserve_whitespace setting which keeps whitespace inside the listed tags as-is. Defaults to {"pre"}, which only has an effect if pre is added to the allowed tags.
Added the embedded_hosts setting which allows embedding content such as iframe elements from the listed hosts.
Added the drop_disallowed_contents setting which removes disallowed tags including their contents.
Fixed crashes when feeding control characters into the sanitizer.
Fixed crashes when feeding control characters into the sanitizer.
Fixed a problem with an overly specific test.
Your coding agent can read these notes before it upgrades. Set up the MCP server →
Vulnerability: Fixed an issue where normalizing unicode too late in the process would keep disallowed tags when using specially crafted HTML. Fixed in…
Vulnerability: Fixed an issue where normalizing unicode too late in the process would keep disallowed tags when using specially crafted HTML. Fixed in 2.4.2.
Fixed missing whitespace while merging adjacent tags.
Raised the minimum lxml-html-clean version to 0.4 because of a reported vulnerability. We were already compatible, but enforcing the lower bound makes sense.
This project publishes no release notes. Between 2.4.3 and 2.4.4 there were 3 commits, 3 of them substantive:
This project publishes no release notes. Between 2.4.3 and 2.4.4 there were 3 commits, 3 of them substantive:
This project publishes no release notes. Between 2.4.2 and 2.4.3 there were 4 commits, 4 of them substantive:
This project publishes no release notes. Between 2.4.2 and 2.4.3 there were 4 commits, 4 of them substantive:
This project publishes no release notes. Between 2.4.1 and 2.4.2 there were 2 commits, 2 of them substantive:
This project publishes no release notes. Between 2.4.1 and 2.4.2 there were 2 commits, 2 of them substantive:
This project publishes no release notes. Between 2.3.1 and 2.4.1 there were 4 commits, 4 of them substantive:
This project publishes no release notes. Between 2.3.1 and 2.4.1 there were 4 commits, 4 of them substantive:
lxml[html_clean] (#39)lxml[html_clean] and use lxml_html_clean (#41)Fixed an edge case where br tag attributes weren't removed if the br tag appears first.
Fixed an edge case where br tag attributes weren't removed if the br tag appears first.
Updated the lxml dependency to 5.2 and added the now-required lxml[html_clean] extra.
This project publishes no release notes. Between 1.9.3 and 2.3.1 there were 24 commits, 23 of them substantive:
This project publishes no release notes. Between 1.9.3 and 2.3.1 there were 24 commits, 23 of them substantive:
…and 3 more.
Avoided adding whitespace when merging tags of the same type.
Avoided adding whitespace when merging tags of the same type.
Updated the tests.
Switched from black to the ruff formatter.
Changed keep_normalized_whitespace to preserve whitespace at the tail of tags, not just between tags.
Changed keep_normalized_whitespace to preserve whitespace at the tail of tags, not just between tags.
Changed the parameters of normalize_whitespace_in_text_or_tail to be keyword-only.
Added a test for a type of misconfiguration.
Added a test for a type of misconfiguration.
Changed the sanitizer configuration validation to not allow unexpected data types in tags, empty, separate, whitespace and attributes.
Raised the minimum Python version to 3.7. Added Python 3.10, 3.11.
Raised the minimum Python version to 3.7. Added Python 3.10, 3.11.
Raised the minimum lxml version to the current 4.9.1.
Switched from Travis CI to GitHub actions. Added Python 3.9 to the CI matrix.
Renamed the main branch to main.
Switched to a declarative setup.
Fixed a whitespace dependency in the testsuite.
Switched to hatchling and ruff.
Made behavior-altering arguments to normalize_overall_whitespace keyword-only.
This project publishes no release notes. Between 1.9.2 and 1.9.3 there were 2 commits, 2 of them substantive:
This project publishes no release notes. Between 1.9.2 and 1.9.3 there were 2 commits, 2 of them substantive:
This project publishes no release notes. Between 1.9.1 and 1.9.2 there were 17 commits, 17 of them substantive:
This project publishes no release notes. Between 1.9.1 and 1.9.2 there were 17 commits, 17 of them substantive:
This project publishes no release notes. Between 1.7.3 and 1.9.1 there were 22 commits, 18 of them substantive:
This project publishes no release notes. Between 1.7.3 and 1.9.1 there were 22 commits, 18 of them substantive:
Added Python 3.8 to the CI matrix.
Added Python 3.8 to the CI matrix.
Be able to keep the <style> tag by adding it to tags.
Added a style check to the CI matrix.
Actually added support for customizing lxml's autolinking behavior using a dictionary argument.
Actually added support for customizing lxml's autolinking behavior using a dictionary argument.
Stopped removing explicitly allowed attributes.
Removed id from allowed attributes of <a> tags to provide an additional layer of defense against DOM clobbering attacks.
Added an element preprocessor which assigns the id value to the name attribute of anchors if name isn't set or empty. This should provide additional backwards compatibility making the id removal less of a problem when using named anchors.
This project publishes no release notes. Between 1.7.2 and 1.7.3 there were 5 commits, 4 of them substantive:
This project publishes no release notes. Between 1.7.2 and 1.7.3 there were 5 commits, 4 of them substantive:
Nothing published for this version
Nothing published for this version
Added a system check which validates sanitizer configurations early when using Django.
Added a system check which validates sanitizer configurations early when using Django.
Fixed an edge case where passing in an empty allowed tags list would unexpectedly and silently not remove any tags at all (because that's the way lxml's cleaner works).
Changed the sanitizer tags, empty and separate options to also accept any iterable, not just sets.
Changed the lru_cache import in the Django module to try functools first.
Fixed the tag merging to also check tags in empty. This means that e.g. consecutive <hr> tags are also merged now when using the default settings.
Made it possible to override the set of tags processed as whitespace. The default set is {"br"} which preserves the current behavior of stripping breaks from the beginning or end of tags' content.
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Fixed another edge case where a tag which is allowed to be empty was erroneously removed if it contained not only whitespace but also a tag.
Fixed another edge case where a tag which is allowed to be empty was erroneously removed if it contained not only whitespace but also a <br> tag.
Fixed a few edge whitespace normalization edge cases and a bug where removing an empty tag removed all whitespace.
Fixed a few edge whitespace normalization edge cases and a bug where removing an empty tag removed all whitespace.
Added black for automatically formatting the Python code.
By default, links with target="_blank" get an additional rel="noopener" attribute (Article by Mathias Bynens). If you're overriding the list of allowed attributes for anchor tags you must add rel to your list.
Corrected the required lxml version in install_requires.
Corrected the required lxml version in install_requires.
Added comments and testing for more edge cases.
Changed the cleaner to not drop form elements; instead, <form> is converted to <p>, and form elements are preserved.
Added an is_mergeable hook for conditionally preventing the merging of adjacent elements.
Fixed a case where paragraphs were allowed inside paragraphs (which was never the idea).
Fixed a case where tags with content between them were erroneously merged.
Fixed a case where tags with content between them were erroneously merged.
Added a tox.ini file for running style checks and tests.
Replaced REPLACEMENTS and element_filters with the more general element_preprocessors and element_postprocessors settings.
Removed the restriction that <span> tags are never allowed.
Nothing published for this version
Fixed the erroneous removal of all whitespace between adjacent elements.
Fixed the erroneous removal of all whitespace between adjacent elements.
Fixed a few occasions where <br> tags were erroneously removed.
Back to beautifulsoup4 for especially broken HTML respectively HTML with Emojis on macOS.
Used a <div> instead of <anything> to wrap the document (since beautifulsoup4 does not like custom tags too much)
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Added html_sanitizer.django.get_sanitizer to provide an official way of configuring HTML sanitizers using Django settings.
Added html_sanitizer.django.get_sanitizer to provide an official way of configuring HTML sanitizers using Django settings.
.. _feincms-cleanse: https://pypi.python.org/pypi/feincms-cleanse/ .. _html-sanitizer: https://pypi.python.org/pypi/html-sanitizer/
Initial public release.
One column per quarter.