Purification


Raw HTML and purification #

Markdown lets authors write HTML inline, so a Markdown field is an HTML field wearing a disguise. Purify HTML is on by default: entry.body.html is run through HTML Purifier after parsing, using the same defaults as Craft’s own HTML fields, which means YouTube and Vimeo iframes survive and <script> doesn’t.

Two things to know about how that works here:

  • It runs at output, not on save. Craft’s CKEditor field purifies as it stores, because what’s stored is HTML. Here the stored value is Markdown source, and purifying that would mangle it: autolinks like <https://example.com> and < inside code fences are not markup. So it happens each time .html is rendered, and the raw Markdown is never touched.
  • |md bypasses it. {{ entry.body.html }} is purified. {{ entry.body|md }} runs Craft's filter over the raw value and isn’t. That’s deliberate, since .raw has to stay pristine, but it means the protection lives on one particular path. |marky is on that path, |md isn’t.

To change what’s allowed through, drop a JSON config file in config/htmlpurifier/ and select it in the field’s settings, exactly as you would for a CKEditor field:

JSON
{
  "HTML.SafeIframe": true,
  "URI.SafeIframeRegexp": "%^(https?:)?//(www\\.youtube\\.com/embed/|player\\.vimeo\\.com/video/|maps\\.google\\.com/)%"
}

Turning Purify HTML off renders exactly what authors type, scripts included. Reasonable when the only people editing are the ones who could edit templates anyway.

Encoding instead #

Encode HTML is the stricter option, and a different one. Purifying parses the HTML and then drops what isn’t safe. Encoding never lets it be HTML at all: an author who types <em>tag</em> gets those characters back on the page rather than an emphasis, and a <script> shows up as text.

Use it where HTML has no business being. A strapline, a caption, a field authored by people you’d rather not hand an <iframe> to. Markdown itself carries on working, so **bold** is still bold. It’s only the raw HTML that goes.

Encoding forces Craft’s pre-encoded parser, which is Traditional Markdown with the escaping it would otherwise do inside code taken out. Without that, a fenced block would come back showing &amp;lt; where the author typed <. The flavour selector is disabled while Encode HTML is on for that reason, and .flavour reports pre-encoded.

The two settings are independent, and belt-and-braces is fine: encoding removes the HTML, purifying then sanitises whatever the parser itself produced.