<script> tags follow unintuitive parsing rules that can break a webpage in surprising ways. Fortunately, it’s relatively straightforward to escape JSON for script tags. Just do this Replace < with \x3C or \u003C in JSON strings. In PHP, use json_encode($data, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES) for safe JSON in <script> tags. In WordPress, use wp_json_encode with the same flags. You don’t have to take my word for it, the HTML standard recommends this type of escaping: The easiest and safest … is to always escape an ASCII case-insensitive match for “<!--” as “\x3C!--“, “<script” as “\x3Cscript“, and “</script” as “\x3C/script“… This post will dive deep into the exotic script tag parsing rules in order to understand how they work and why this is the appropriate way to escape JSON. What’s so gnarly about a script tag? Script tags are used to embed other languages in HTML. The most common example is JavaScript: This is great, JavaScript can be embedded directly. Imagine if script tags required HTML escaping: In fact, script tags can contain any language (not necessarily JavaScript) or even arbitrary data. In order to support this behavior, script tags have special parsing rules. For the most part, the browser accepts whatever is inside the script tag until it finds the script close tag </script>1. So, what happens when we embed this perfectly valid JavaScript that contains a script close tag? Oops! We can see that </script> was part of a JavaScript string, but the browser is just parsing the HTML. This script element closes prematurely, resulting in the following tree: ├─SCRIPT │ └─#text console.log(' └─#text ') Ok, let’s use json_encode() and we should be all set: Now we’ve got this HTML: </script> has become <\/script>. The JavaScript string value is preserved and the script element does not close prematurely. Perfect, right? Not so fast, things are about to get messy Let’s expand with a more complex example. Here’s some data used by an imaginary HTML library. We’l...
First seen: 2025-08-09 03:31
Last seen: 2025-08-09 10:33