Library / SDK
flavorjones/loofah avatar
flavorjones/loofah

Loofah and the difference between stripping a tag and pruning it

Ruby library for HTML/XML transformation and sanitization

1,000 stars146 forksRubyMIT

At a glance

What is it?
Loofah is a Ruby library for transforming and sanitizing HTML and XML, built on Nokogiri, that offers four built-in scrubbers and a small API for writing your own. Its documentation is unusually candid about the trap in the middle of it, that Loofah.document still means the HTML4 parser and may not in a future release.
Who is it for?
Adopt Loofah if your Ruby application renders user supplied HTML, since it gives you four ready made scrubbers and a way to write your own, on top of a parser that already knows how browsers treat malformed markup. Do not adopt it if you need a fine grained per tag and attribute policy, since the README itself points to rgrove/sanitize for that flexibility, and do not copy the Loofah.document alias into new code without thinking.
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 2 days ago.
What is it written in?
Mainly Ruby, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A transformation framework that happens to sanitize

Loofah is a general library for manipulating and transforming HTML and XML documents and fragments, built on top of Nokogiri, which the documentation describes as a stable, well maintained parser for XML, HTML4 and HTML5. On top of that general capability, Loofah includes HTML sanitizers based on the html5lib safelist, which the documentation frames as a specific application of the general transformation functionality.

That framing explains the design. The built-in sanitizers are not hard-coded special cases; they are scrubbers, the same mechanism you would use to write a transformation of your own. The project makes the point bluntly, telling you that if you do not like the built-in scrubbers you can build your own.

The audience is Ruby applications that take markup from somewhere they do not control and put it in front of a user. That covers comment systems, rich text fields, imported documents, and anything that renders user submitted HTML in a Rails view, where the naive alternatives, escaping everything or stripping all tags, both produce broken output.

The library also has an Active Record extension, the loofah-activerecord gem, for sanitizing HTML at the model layer, and the documentation notes that Loofah is what replaces the strip_tags and sanitize view helpers in Rails. The surrounding ecosystem is documented too, with the docs hosted on rubydoc.info and a mailing list at loofah-talk on Google Groups.

The repository is a conventional Ruby gem layout, with a gemspec, a Rakefile, a Gemfile, a lib/ directory, a test/ directory, a benchmark/ directory, a CHANGELOG.md, a SECURITY.md and both a .rubocop.yml and a .rubocop_todo.yml.

Four scrubbers, and the traces they leave behind

The built-in sanitizing transformations differ in a way that matters more than it first appears, and the documentation describes each one in terms of what is left in the output.

Strip removes unsafe tags but leaves the inner text behind. Prune removes the unsafe tags and their subtrees, so no trace of them remains. Escape escapes the unsafe tags and their subtrees, which leaves a lot of < and > entities in the document. Whitewash removes all attributes and namespaced nodes, leaving the structure of the markup and nothing else.

The choice between strip and prune is a decision about what an attacker learns. If you strip a script tag, its source code stays in the output as text, which is harmless when the output is rendered as HTML but leaks the script source to anyone who reads the page source. If you prune, the script disappears entirely. For a comment body that is later displayed back to the author, that difference is visible.

Whitewash is the most aggressive of the four and is worth understanding before reaching for it, because it discards attributes as well as unsafe elements, which usually means the formatting the user typed is gone as well.

Beyond sanitizing, there are transformations for other common needs. Loofah can add the nofollow attribute to all hyperlinks, add target=_blank to all hyperlinks, and remove unprintable characters from text nodes. There is a specialised transformation for a specific structural annoyance, closing a p tag and opening a new one wherever a br br sequence appears inside a p, which is a browser parsing quirk that shows up when users paste content.

There is also plain text output. The to_s method returns HTML, the text method returns plain text, and to_text does the right thing with whitespace around block level and line break elements.

Loofah.document is not what you think it is

The most important paragraph in the README is the one about fragments versus documents, and the one after it about HTML4 versus HTML5.

For fragments, the rule of thumb is that unless you expect a DOCTYPE and a single root node, you have a fragment rather than a document, and for HTML the further hint is that documents have html and body tags while fragments usually do not. HTML fragments are parsed with Loofah.html5_fragment or Loofah.html4_fragment, and the result is not wrapped in html or body tags, has no DOCTYPE declaration, silently ignores head elements, and allows multiple root nodes. HTML documents parsed with Loofah.html5_document or Loofah.html4_document have a DOCTYPE along with html, head and body tags. XML fragments parsed with Loofah.xml_fragment have no DOCTYPE and allow multiple roots, while XML documents parsed with Loofah.xml_document have a DOCTYPE and a single root node.

The HTML4 versus HTML5 part is the trap. Today, Loofah.document and Loofah.fragment are aliases to .html4_document and .html4_fragment, and the HTML classes are aliased to the HTML4 classes. The documentation warns that in a future version of Loofah these methods and classes may switch to using Nokogiri's HTML5 parser on platforms that support it, and it strongly recommends explicitly using .html5_document or .html5_fragment unless you have a compelling reason not to. If you are sure you need the HTML4 parser, you should call .html4_document or .html4_fragment explicitly to avoid breakage in a future version.

That is a deprecation in everything but name, and code that works today can change behaviour on a version bump. The workaround is cheap, one method name per call site, and the documentation is unusually clear about why.

The scrub! method is the other half of the API. Loofah injects a scrub! method that takes either a symbol naming a built-in scrubber or a Loofah::Scrubber object, and it modifies the document in place.

JRuby and old Nokogiri lose the HTML5 parser

There is a warning at the top of the HTML4 versus HTML5 section that HTML5 functionality is not available on JRuby, or with versions of Nokogiri below 1.14.0.

That constraint propagates through the whole library. On the JVM, or on a pinned old Nokogiri, the html5_document and html5_fragment methods are not available, which means the strongly recommended path in the documentation cannot be followed, and the explicit html4 calls become mandatory rather than optional. The README links the underlying feature request, an issue on Nokogiri asking for an HTML5 parser for JRuby, so the situation is understood to be a limitation of the parser rather than of Loofah.

The same request explains why the class aliases are set up the way they are. The Loofah::HTML4::Document and Loofah::HTML4::DocumentFragment classes are aliased as Loofah::HTML::Document and Loofah::HTML::DocumentFragment for now, with the phrase for now doing a lot of work, and the plan is that the generic HTML classes eventually follow the platform's best available parser.

For a team, the practical consequence is that the Ruby version and the runtime version of your application interact. A service that must run on JRuby, or that pins Nokogiri for another dependency's sake, has to design its sanitization around the HTML4 parser today and plan for the switch later, when the alias will start returning HTML5 parsed documents on platforms that support it.

A Scrubber is a block or a method, and scrub! applies it

The custom transformation mechanism is small enough to grasp in a minute. Loofah::Scrubber represents the document transformation, and it can be created either by wrapping a block or by implementing a method.

The block form is the quickest way to write a one-off rule, and the example in the documentation turns spans into divs:

ruby
span2div = Loofah::Scrubber.new do |node|
  node.name = "div" if node.name == "span"
end

A scrubber receives nodes as it walks the document, so the same mechanism that implements a sanitizer can implement a normaliser, a link rewriter or a table converter. The documentation is explicit that this is the general side of the library, the transformation framework, and the sanitizers are a specific application of it.

Applying it is a single call. The scrub! method takes a symbol for a built-in scrubber or a Scrubber object for a custom one, and modifies the document in place. The example in the README shows the whole loop for a fragment that contains a safe div and an unsafe script:

ruby
unsafe_html = "ohai! <div>div is safe</div> <script>but script is not</script>"

doc = Loofah.html5_fragment(unsafe_html).scrub!(:prune)
doc.to_s    # => "ohai! <div>div is safe</div> "

The same object then answers text for a plain text rendering, and the README notes that to_text is available as well, which does the right thing with whitespace around block level and line break elements.

sanitize and rails-html-sanitizer, and which one to pick

The Compare and Contrast section of the README names two neighbouring projects, and the difference between them is a decision you can make from the documentation rather than by experiment.

rgrove/sanitize is another Ruby library for HTML sanitization built on top of Nokogiri, and the README describes it as providing a bit more flexibility on the tags and attributes being scrubbed. If your requirement is a fine grained policy, a different allow list per context, or configurable attribute handling, that is the library to evaluate. Loofah's built-in sanitizers are deliberately built on the html5lib safelist, and a safelist is exactly what you do not want when you need to deviate from one.

rails/rails-html-sanitizer is built on top of Loofah and provides some useful extensions and additional flexibility in HTML sanitization, which makes it the natural choice for a Rails application that wants Loofah's behaviour with the view helper conveniences.

The Active Record extension, loofah-activerecord, covers the third common case, sanitizing at the model layer rather than in the view.

So the choice is not Loofah or nothing. It is Loofah when you want the general transformation framework with a sane safelist default, sanitize when the policy is the hard part, and rails-html-sanitizer when you are in Rails and want the extensions.

MIT, a security policy and a July 2026 release

The licence is MIT, with the text in MIT-LICENSE.txt at the root of the repository, and that is one of the simplest adoption questions a Ruby library can ask.

The repository does take security seriously in the process sense. There is a SECURITY.md at the root, a CHANGELOG.md, a set of RuboCop configurations including a todo file for the rules not yet satisfied, and a CI badge for the main branch. A benchmark/ directory at the root suggests the maintainers care about the performance of the transformation step, which matters for a library that sits in the request path of a page render.

On maintenance, the repository is not archived. The last push was on 2026-10-08, and the recent releases are v2.25.2 on 2026-07-15, v2.25.1 on 2026-03-17 and v2.25.0 on 2025-12-15. The version numbering has stayed on the 2.x line for a long time, which is a good sign for a library that other gems depend on, since the semver major version has not had to move.

One structural detail explains the stability. The library is a thin layer over Nokogiri, and the HTML5 versus HTML4 work is really a question about what Nokogiri supports, tracked on the Nokogiri side. Loofah's own releases are therefore mostly about the wrapper, the scrubbers and the compatibility with Nokogiri changes, which is a small surface to maintain and a good one to depend on.

Editorial conclusion

Adopt Loofah if your Ruby application renders user supplied HTML, since it gives you four ready made scrubbers and a way to write your own, on top of a parser that already knows how browsers treat malformed markup. Do not adopt it if you need a fine grained per tag and attribute policy, since the README itself points to rgrove/sanitize for that flexibility, and do not copy the Loofah.document alias into new code without thinking. Verify three things first: that you call.html5_fragment or.html5_document explicitly, since the current aliases use the HTML4 parser and the documentation warns they may switch later, that you are not on JRuby and have Nokogiri 1.14.0 or later if you want HTML5, and which of the four scrubbers matches your threat model, because strip and prune leave visibly different traces in the output. The last push was on 2026-10-08 and the newest release is v2.25.2 from 2026-07-15.

Frequently asked questions

What is Loofah used for?

It is a Ruby library for manipulating and transforming HTML and XML documents and fragments, built on Nokogiri, and it includes HTML sanitizers based on the html5lib safelist. It is also what replaces the strip_tags and sanitize view helpers in Rails.

What is the difference between the strip and prune scrubbers in Loofah?

Strip removes unsafe tags but leaves their inner text behind, while prune removes the unsafe tags and their subtrees so no trace remains. Escape escapes them instead, leaving entities in the output, and whitewash removes all attributes and namespaced nodes.

Should I use Loofah.html5_fragment or Loofah.document?

Use the explicit html5_fragment or html5_document methods. Loofah.document and Loofah.fragment are currently aliases for the HTML4 versions, and the documentation warns they may switch to the HTML5 parser in a future version, which would change behaviour without any change in your code.

Does Loofah support HTML5 on JRuby?

No. HTML5 functionality is not available on JRuby, or with Nokogiri versions below 1.14.0, so on those platforms you have to call the html4 methods explicitly. The underlying parser request is tracked as an issue on Nokogiri.

How do I write a custom Loofah transformation?

Create a Loofah::Scrubber, either by wrapping a block that receives each node or by implementing a method, then apply it with the scrub! method, which takes a symbol for a built-in scrubber or a Scrubber object and modifies the document in place.

What licence is Loofah released under?

MIT, with the text in MIT-LICENSE.txt at the root of the repository. The last push was on 2026-10-08 and the newest release is v2.25.2 from 2026-07-15, and the repository also ships a SECURITY.md and a CHANGELOG.md.

Official sources

  1. flavorjones/loofah on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
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.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/flavorjones-loofah.svg)](https://hysenlabs.com/projects/flavorjones-loofah)

Was this useful?