JSON Escaping and Special Characters

Updated: August 8, 2026

Which characters need a backslash, which do not, and the escaping mistakes that produce unparseable documents or security holes.

, which terminates the tag regardless of JSON quoting. Escape the forward slash as <\\/script> or replace < with \\u003c. This is an HTML problem rather than a JSON one, and it is a real XSS vector."}}]}

The Complete Escape Table

Escaping applies only inside strings. Outside a string there is nothing to escape, because the only things that can appear there are structural characters and numbers.

SequenceCharacterRequired?
\"Double quoteYes
\\BackslashYes
\nLine feedYes
\rCarriage returnYes
\tTabYes
\bBackspaceYes
\fForm feedYes
\/Forward slashOptional
\uXXXXAny code point, by hexOptional

That is the entire list. A backslash followed by anything else — \x41, \0, \' — is invalid JSON, even though several of those are legal in JavaScript, Python or C string literals.

The Three Mandatory Cases

Double quotes terminate a string, so one appearing inside must be escaped:

{ "quote": "She said "hello"" }        // invalid
{ "quote": "She said \"hello\"" }      // valid

Single quotes need no escaping when they appear inside a string — they are ordinary characters. They are only forbidden as delimiters.

Backslashes are the escape character, so a literal one must be doubled. Windows paths and regular expressions hit this constantly:

{ "path": "C:\Users\Alice" }            // invalid: \U and \A are not escapes
{ "path": "C:\\Users\\Alice" }          // valid → C:\Users\Alice
{ "regex": "\\d{4}-\\d{2}" }             // valid → \d{4}-\d{2}

Control characters below U+0020 cannot appear literally. A real line break inside a string is a syntax error:

{ "note": "line one
line two" }                              // invalid

{ "note": "line one\nline two" }         // valid

Unicode

JSON strings are Unicode. Non-ASCII characters may be written literally provided the document is UTF-8, which it should be:

{ "name": "José", "city": "Bengaluru", "reaction": "🎉" }   // valid
{ "name": "Jos\u00e9" }                                     // identical value

Both forms parse to the same string. Which you get depends on your serialiser — Python escapes by default unless you pass ensure_ascii=False, PHP escapes unless you pass JSON_UNESCAPED_UNICODE, while JavaScript's JSON.stringify leaves characters literal. This is purely a readability choice.

One real subtlety: \uXXXX is limited to four hex digits, so it can only address code points up to U+FFFF. Characters beyond that — most emoji, many historic scripts — must be written as a surrogate pair:

"🎉"  escaped as  "\ud83c\udf89"     // two \u escapes, one character

This is why naive string-truncation code corrupts emoji: cutting between the two halves of a surrogate pair leaves an unpaired surrogate, which is not valid Unicode and which some parsers will reject outright.

Nested JSON: The Escaping Explosion

When a JSON document is stored as a string inside another JSON document, every quote in the inner document needs escaping. This happens more than it should — in message queues, in audit log columns, in webhook payloads that forward a provider's raw body.

{
  "event": "payment.created",
  "payload": "{\"amount\":1999,\"currency\":\"INR\"}"
}

It parses correctly, but you must now parse twice, and each additional nesting level doubles the backslashes. Two levels deep is already close to unreadable:

"{\\\"amount\\\":1999}"    // three levels — genuinely unmaintainable

Where you control the format, nest the object directly instead of stringifying it:

{
  "event": "payment.created",
  "payload": { "amount": 1999, "currency": "INR" }
}

The usual justification for the string form is that the inner payload's shape is unknown or must be preserved byte-for-byte — for signature verification, say. That is a legitimate reason. "It was easier at the time" is not.

Embedding JSON in HTML

Putting JSON inside a <script> tag introduces a hazard that has nothing to do with JSON's own rules, and it is a genuine cross-site scripting vector.

<script>
  var data = {"comment": "</script><script>alert(1)</script>"};
</script>

The string </script> ends the tag as far as the HTML parser is concerned, no matter that it sits inside JSON quotes. The browser never reaches your JSON — it just runs the injected script. Escaping the slash defuses it, because <\/script> is not a closing tag to HTML but is still a forward slash to JSON:

JSON.stringify(data).replace(/</g, "\\u003c")

This is the historical reason PHP escapes forward slashes by default — a blunt instrument aimed at exactly this problem. The safer modern approach is to avoid inline data entirely: put it in <script type="application/json">, which browsers do not execute, and read it with JSON.parse(element.textContent).

Let a Serialiser Do It

Nearly every escaping bug comes from building JSON by string concatenation. Do not:

// Broken the moment name contains a quote, backslash or newline
const json = '{"name": "' + name + '"}';

// Correct — the serialiser handles every case
const json = JSON.stringify({ name });

The same applies in every language: json.dumps(), json_encode(), json.Marshal(). Hand-built JSON is to escaping what hand-built SQL is to injection — the failure mode is identical, and so is the fix.

Frequently Asked Questions

Which characters must be escaped in JSON?

Three things are mandatory: the double quote, the backslash, and any control character below U+0020 — which includes literal newlines and tabs. Everything else, including forward slashes and non-ASCII characters, may be written literally.

Why do I see \/ instead of / in JSON output?

Escaping the forward slash is optional and legal. PHP does it by default, a legacy behaviour intended to make embedding JSON inside HTML script tags safer. It parses identically either way. Pass JSON_UNESCAPED_SLASHES in PHP to turn it off.

How do I put a Windows file path in JSON?

Double every backslash: "C:\\Users\\Alice" represents the path C:\Users\Alice. The backslash is JSON's escape character, so a literal one must itself be escaped. Alternatively use forward slashes, which Windows accepts in most contexts.

Do I need to escape emoji and accented characters?

No. JSON strings are Unicode, so é and 🎉 can be written literally as long as the document is UTF-8. Some serialisers escape them anyway — Python's ensure_ascii=True is on by default — but this is a readability choice, not a correctness requirement.

How should I escape JSON that goes inside an HTML script tag?

The dangerous sequence is </script>, which terminates the tag regardless of JSON quoting. Escape the forward slash as <\/script> or replace < with \u003c. This is an HTML problem rather than a JSON one, and it is a real XSS vector.

Related Resources

Related Resources