The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.

Safe HTML with HTML::Blitz::Builder

Lukas Mai

2026-03-17

Bad, unsafe, terrible

my $html = "";
$html .= "<p>Hello, $user!</p>\n";
$html .= "<ul>\n";
for my $item (@list) {
    $html .= "<li>$item</li>\n";
}
$html .= "</ul>\n";
print $html;

Good, safe, lovely

use HTML::Blitz::Builder qw(to_html mk_elem);

my @html;
push @html, mk_elem('p', "Hello, $user!");
my @list;
for my $item (@list) {
    push @list, mk_elem('li', $item);
}
push @html, mk_elem('ul', @list);
print to_html(@html);

Example 1: text

use v5.36;
use HTML::Blitz::Builder qw(to_html mk_elem);

say to_html("I <3 HTML!");
I &lt;3 HTML!

Example 2: nested elements

use v5.36;
use HTML::Blitz::Builder qw(to_html mk_elem);

say to_html(
    mk_elem('p',
        "Everything is possible at ",
        mk_elem('a', { href => 'https://zombo.com/' }, "Zombocom"),
    ),
);
<p>Everything is possible at <a href=https://zombo.com/>Zombocom</a></p>

Example 3: void elements

use v5.36;
use HTML::Blitz::Builder qw(to_html mk_elem);

say to_html(
    mk_elem('p', "Hello"),
    mk_elem('hr'),
    mk_elem('img', {
        src => 'kitten.jpg',
        alt => "photo of a cute kitten",
    }),
    mk_elem('script', { src => 'main.js' }),
);
<p>Hello</p><hr><img alt="photo of a cute kitten" src=kitten.jpg><script src=main.js></script>

Example 4: non-elements

use v5.36;
use HTML::Blitz::Builder qw(to_html mk_elem mk_doctype mk_comment);

say to_html(
    mk_doctype,
    mk_comment("yay"),
    "etc",
);
<!DOCTYPE html><!--yay-->etc

Example 5: non-representable comment

use v5.36;
use HTML::Blitz::Builder qw(to_html mk_elem mk_doctype mk_comment);

say to_html(
    mk_doctype,
    mk_comment("move A --> B"),
    "etc",
);
HTML comment cannot contain '-->': 'move A --> B' at /tmp/slide-snippet-RPOK15 line 4.

Example 6: raw script

use v5.36;
use HTML::Blitz::Builder qw(to_html mk_elem);

say to_html(
    mk_elem('script', <<~'_SCRIPT_'),
        if (a < b && c > d) {
            foo();
        }
        _SCRIPT_
);
<script>if (a < b && c > d) {
    foo();
}
</script>

Example 7: non-representable script

use v5.36;
use HTML::Blitz::Builder qw(to_html mk_elem);

say to_html(
    mk_elem('script', <<~'_SCRIPT_'),
        const foo = "</SCRIPT>";
        _SCRIPT_
);
<script> tag cannot contain '</SCRIPT>' at /tmp/slide-snippet-d4AKNb line 4.

Example 8: bizarre script

use v5.36;
use HTML::Blitz::Builder qw(to_html mk_elem);

say to_html(
    mk_elem('script', <<~'_SCRIPT_'),
        /* <!-- <script> */
        const foo = "</SCRIPT>";
        _SCRIPT_
);
<script>/* <!-- <script> */
const foo = "</SCRIPT>";
</script>

Thank you!

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.