Unescaped output
Regular #{...} interpolation escapes the value: it replaces <, >, & and quotes with safe codes so that text stays text. Sometimes a variable holds ready-made HTML that should become markup. For that there is unescaped output — !{...} and !=.
Ready-made HTML in a variable
Suppose build.js passes a promo line with a strong tag:
// build.js
const html = pug.renderFile("src/index.pug", {
pretty: true,
promo: "<strong>10% off</strong> coffee before noon",
});If you output it with regular interpolation in src/index.pug:
p.promo #{promo}the tags appear on the page as text:
<p class="promo"><strong>10% off</strong> coffee before noon</p>Output with !{} and !=
An exclamation mark turns escaping off. Change src/index.pug:
p.promo !{promo}
p.promo!= promoBoth lines do the same: !{...} inserts inside text, != sets the whole content of the tag. Now "10% off" is bold:
<p class="promo"><strong>10% off</strong> coffee before noon</p>
<p class="promo"><strong>10% off</strong> coffee before noon</p>| Syntax | Escapes | Where it goes |
|---|---|---|
#{...} | Yes | Inside text |
tag= ... | Yes | The whole tag content |
!{...} | No | Inside text |
tag!= ... | No | The whole tag content |
The danger: XSS
XSS (cross-site scripting) is an attack where someone submits text with HTML code to a site, and that code runs in other visitors' browsers. For example, instead of a coffee review someone writes:
<img src=x onerror="alert('Hacked')">If the review reaches the template in the review variable and is output with escaping:
p.review= reviewvisitors see harmless text:
<p class="review"><img src=x onerror="alert('Hacked')"></p>But the line p.review!= review inserts the review as markup:
<p class="review"><img src=x onerror="alert('Hacked')"></p>This result is shown as plain text, not as a page: in a browser the image fails to load, onerror fires and someone else's JavaScript runs. Instead of alert it could be code that steals the visitor's data.
When unescaped output is fine
Insert unescaped only HTML you wrote yourself or got from a trusted source, for example your own Markdown files turned into HTML at build time. Everything visitors type — reviews, names, comments — output with = and #{...}.
Common mistake
Using != "just in case", so that quotes or ampersands do not turn into codes. The browser displays escaped characters correctly anyway: & looks like & on the page. Turn escaping off only where the variable really holds HTML you want.