Quick Start
This tutorial takes you from an empty Ruby file to a small, realistic sanitization pipeline: parse untrusted HTML, remove dangerous markup, add link attributes, apply a custom transformation, and produce a plain-text preview. Each step shows the expected result.
The examples use the HTML5 parser. If Loofah.html5_support? is false on your platform (for example on JRuby), replace html5 with html4 in every method name; see Installation.
1. Parse a Fragment
User-submitted HTML is almost always a fragment: a piece of markup without <html>, <head>, or <body>. Parse it with Loofah.html5_fragment:
require "loofah"
input = <<~HTML
<p>Hello <b>world</b>!</p>
<script>alert('pwned')</script>
<p onclick="steal()">Click <a href="javascript:alert(1)">here</a>
or <a href="https://example.com">there</a>.</p>
HTML
fragment = Loofah.html5_fragment(input)
fragment.class # => Loofah::HTML5::DocumentFragment
Nothing has been removed yet. fragment is a Nokogiri fragment with Loofah's extra methods, so you can already query it:
fragment.css("a").map { |a| a["href"] }
# => ["javascript:alert(1)", "https://example.com"]
2. Sanitize It
Call scrub! with one of the built-in sanitizers. :prune removes unsafe elements together with everything inside them:
fragment.scrub!(:prune)
puts fragment.to_s
The output is similar to:
<p>Hello <b>world</b>!</p>
<p>Click <a>here</a>
or <a href="https://example.com">there</a>.</p>
What happened:
<script>is not on the safelist, so it was removed with its content.onclickis not an allowed attribute, so it was removed from the<p>.href="javascript:alert(1)"failed the URI check, so thehrefattribute was removed (the<a>element itself is allowed and stays).
scrub! modifies the fragment in place and returns it, so calls can be chained.
3. Add Link Attributes
Chain link scrubbers to harden the remaining links:
fragment.scrub!(:nofollow).scrub!(:noopener)
fragment.css("a").map(&:to_html)
# => ["<a rel=\"nofollow noopener\">here</a>",
# "<a href=\"https://example.com\" rel=\"nofollow noopener\">there</a>"]
The rel values are appended rather than overwritten, and a value already present is not duplicated. See Link and Text Scrubbers.
4. Write a Custom Scrubber
Suppose your site styles bold text with <strong> instead of <b>. A scrubber is a block that receives every node:
b_to_strong = Loofah::Scrubber.new do |node|
node.name = "strong" if node.name == "b"
end
fragment.scrub!(b_to_strong)
fragment.at_css("strong").to_html # => "<strong>world</strong>"
Custom Scrubbers explains traversal order, Loofah::Scrubber::STOP, and class-based scrubbers.
5. Produce Plain Text
For notification emails or previews, convert the fragment to text. to_text places newlines around block elements and <br>:
Loofah.html5_fragment("<h1>Title</h1><div>Content<br>Next line</div>").to_text
# => "\nTitle\n\nContent\nNext line\n"
Compare with text, which just concatenates text nodes:
Loofah.html5_fragment("<h1>Title</h1><div>Content<br>Next line</div>").text
# => "TitleContentNext line"
Both escape <, >, and & by default so the result is safe to drop into HTML. See Text Output for the encode_special_chars option.
6. Use the One-Line Shorthand
For the common "parse and scrub" case there are shorthand methods:
Loofah.scrub_html5_fragment(input, :prune).to_s
This is exactly Loofah.html5_fragment(input).scrub!(:prune).
Putting It Together
require "loofah"
class CommentSanitizer
B_TO_STRONG = Loofah::Scrubber.new do |node|
node.name = "strong" if node.name == "b"
end
def self.html(input)
Loofah.html5_fragment(input)
.scrub!(:prune)
.scrub!(:nofollow)
.scrub!(:noopener)
.scrub!(B_TO_STRONG)
.to_s
end
def self.preview(input)
Loofah.html5_fragment(input).scrub!(:prune).to_text.strip
end
end
Note: Always run a sanitizing scrubber (
:prune,:strip,:escape, or:whitewash) before relying on the output being safe. The link scrubbers and custom scrubbers do not remove anything dangerous on their own.
Next Steps
- Sanitizing Scrubbers: choose between strip, prune, escape, and whitewash.
- Documents and Fragments: when to parse a full document instead.
- Safelist Reference: exactly which tags, attributes, protocols, and CSS survive.