JSON String Escaping: Characters, Edge Cases, and Common Mistakes
Which characters must be escaped in JSON strings, how Unicode escapes work, and how improper handling leads to bugs or security issues.
Why JSON Needs String Escaping
JSON strings are delimited by double quotes. Any character inside a string that could be misinterpreted as JSON structure — or that cannot be represented literally — must be escaped with a backslash sequence. The JSON specification (RFC 8259) defines exactly which characters require escaping.
Required Escape Sequences
" → double quote (end-of-string delimiter)
\ → backslash (escape character itself)
/ → forward slash (optional but valid)
\b → backspace (U+0008)
\f → form feed (U+000C)
\n → line feed / newline (U+000A)
\r → carriage return (U+000D)
\t → horizontal tab (U+0009)Control characters (U+0000 to U+001F) must be escaped — they cannot appear literally in JSON strings. This includes null bytes, newlines, and tabs.
Unicode Escape Sequences
Any character can be escaped using \uXXXX where XXXX is a four-digit hex code point:
"\u0041" → "A"
"\u00e9" → "é"
"\u4e2d\u6587" → "中文"
"\u0000" → null byte (U+0000)For characters outside the Basic Multilingual Plane (above U+FFFF, like emoji), JSON uses surrogate pairs: two \uXXXX sequences that together encode the character.
// 😀 (U+1F600) as a JSON surrogate pair
"\uD83D\uDE00"Escaping in JavaScript
JSON.stringify() handles escaping automatically. It is the correct way to produce JSON strings from user input:
const userInput = 'He said "hello"\nNew line here';
const json = JSON.stringify(userInput);
// '"He said \"hello\"\nNew line here"'
// NEVER concatenate strings manually:
const bad = '{"name": "' + userInput + '"}'; // BROKEN with special charsUnescaping JSON Strings
To convert a JSON-encoded string back to its raw value, use JSON.parse():
const escaped = '"Hello\\nWorld"'; // the raw JSON string value
JSON.parse(escaped); // → 'Hello\nWorld'Double-Encoding: A Common Mistake
Double-encoding happens when a string that is already JSON-escaped gets escaped again. The result looks garbled:
// First encoding
const once = JSON.stringify('hello "world"');
// '"hello \"world\""'
// Double-encoded (wrong)
const twice = JSON.stringify(once);
// '"\"hello \\\"world\\\"\""'This happens when you store a JSON string in a database and then serialize the containing object again. The fix: parse before re-serializing, or store the raw value rather than the JSON-encoded version.
JSON Injection
If user input is interpolated into a JSON string without proper escaping, an attacker can break out of the string context and inject arbitrary JSON structure:
// Vulnerable template literal
const payload = `{"username": "${userInput}"}`;
// Attacker input: admin", "admin": true, "x": "
// Result:
// {"username": "admin", "admin": true, "x": ""}
// ↑ injected "admin": true propertyAlways use JSON.stringify() to build JSON. Never build JSON by string concatenation or template literals with unescaped values.
forward slash: Optional Escaping
The forward slash / may be escaped as \/ in JSON but is not required to be. Some JSON encoders escape it to prevent </script> sequences from closing script tags when JSON is embedded in HTML:
// Safe for embedding in <script> tags
{"url": "https:\/\/example.com\/path"}
// vs unescaped (still valid JSON)
{"url": "https://example.com/path"}Try it yourself
Escape raw text into a JSON-safe string or unescape a JSON string back to plain text.
Open JSON Escape →