skip to content

What is the )]}', prefix some servers put in front of JSON responses, and what does Angular's HttpClient do with it?

level: middleimportance: nice to knowfreq 22%

answer

  1. JSON loaded as a script
  2. make the body non-executable
  3. stripped before parsing
  4. only for JSON response type
  5. the server adds it, not Angular

basics

~20 s

It is an XSSI guard: prefixing JSON with )]}' and a newline makes the response a syntax error when a hostile page loads it as a script. HttpClient strips it before parsing JSON, so application code never sees it.

solid answer

~40 s

**Cross-site script inclusion (XSSI)**, also called the JSON vulnerability, is an attack in which a hostile page includes your JSON API with a `<script src>` tag, so the victim's browser sends cookies, and tries to observe the data as it executes. Angular's guide notes it worked on older browsers by overriding built-in constructors. It only works if the response is valid JavaScript, so servers conventionally prefix JSON with `)]}',` plus a newline, which makes the body a syntax error as a script. Angular's `HttpClient` knows the convention: when the response type is JSON, both the fetch and XHR backends remove that prefix, with the comma optional, from the start of the body before `JSON.parse`, so application code receives normal parsed data. Angular never adds the prefix; that is the server's job.

code

ts · 17 lines
ts
import {Injectable, inject} from '@angular/core';
import {HttpClient} from '@angular/common/http';

interface Balance {
  balance: number;
}

@Injectable({providedIn: 'root'})
export class AccountsApi {
  private http = inject(HttpClient);

  // The server sends )]}' and a newline before {"balance":1250};
  // HttpClient strips the prefix before JSON.parse, so subscribers get {balance: 1250}.
  balance() {
    return this.http.get<Balance>('/api/balance');
  }
}

go deeper

for a junior

Recall that some servers put )]}' before JSON and that HttpClient removes it automatically.

for a middle

Explain why the prefix works, a script-tag include hits a syntax error, and that stripping happens only for the JSON response type.

for a senior

Place XSSI next to XSRF as the two HTTP-level defences, know the server owns both, and recognise parse failures caused by prefixes with non-JSON response types.

for a principal

Decide whether a modern API still emits the prefix, weighing compatibility with non-Angular clients against a guard that newer browsers make less necessary.

## The attack the prefix defeats A `<script src="…">` tag may load a script from **any** origin, and the browser sends that origin's cookies with the request. **Cross-site script inclusion (XSSI)**, which Angular's security guide also calls the **JSON vulnerability**, abuses this: 1. A hostile page includes `https://your-app.example/api/accounts` as a script. 2. The victim's browser sends the request with the victim's session cookie, and your API returns their data as JSON. 3. The browser executes the response as JavaScript. On older browsers, a page that had overridden built-in object constructors could observe the values as they were created. The attack only works if the JSON body is **executable as JavaScript**. Break that, and the script fails before any data is touched. ## The convention: a non-executable prefix Servers therefore prefix every JSON response with a short string that is a syntax error as JavaScript: ```txt )]}', {"balance": 1250} ``` - As a **script**, the leading `)` is a syntax error, so nothing after it runs. - As **JSON** fetched by your own app, the prefix is simply removed before parsing. The prefix is a server-side choice. Angular does not add it and cannot protect an API that returns raw JSON to script tags; it only makes the prefixed format painless on the client. ## What HttpClient does Both of `HttpClient`'s backends, the default fetch backend and the XHR backend, handle the prefix the same way when the request's **`responseType` is `'json'`**, which is the default: 1. Take the response body as text. 2. Remove a leading `)]}'`, followed by an **optional comma** and a newline, if and only if it is at the very start. 3. Parse the remainder with `JSON.parse`; an empty remainder becomes `null`. | Response body | Value delivered to your code | | :--- | :--- | | `)]}',` newline then `{"a":1}` | `{a: 1}` | | `)]}'` newline then `[1,2]` | `[1, 2]` | | `{"a":1}` with no prefix | `{a: 1}`, unchanged | | prefix with `responseType: 'text'` | the full text, prefix included | The last row matters: stripping belongs to JSON parsing, so asking for `text`, `blob` or `arraybuffer` returns the body exactly as sent. ## When parsing still fails If the body is not valid JSON even after stripping, a successful status still ends in an `HttpErrorResponse` whose `error` describes the parse failure (the XHR backend also attaches the body text). For an error status such as `500`, the body is delivered as a plain string instead. That is useful when an HTML error page arrives where JSON was expected. ## Checking it in practice - In the browser's network panel, the raw response shows the prefix, while the value your subscriber logs does not; that difference is `HttpClient` doing its job. - A parse error on a JSON endpoint that works in other clients often means the endpoint uses a different prefix, or the request asked for `text` and code then called `JSON.parse` itself on a body that still starts with `)]}'`. - Only the exact convention is recognised: `)]}'`, an optional comma, then a newline, at the very start of the body. Anything else is left for `JSON.parse` to reject. ## How it relates to the other defences - XSSI is a **read** attack on JSON; XSRF is a **write** attack on state-changing endpoints. Angular's guide groups them as the two HTTP-level vulnerabilities `HttpClient` helps with, and both must be mitigated primarily on the server. - Modern browsers have closed the constructor-override trick the guide describes, but the prefix remains a cheap, well-known guard that some backends still emit, so interviewers ask whether you recognise it.

  • Does HttpClient strip the prefix when you request responseType: 'text'?
    No. Stripping is part of JSON parsing, so it only runs when the response type is `'json'`. With `'text'` you receive the body exactly as sent, prefix included, and must remove it yourself if you later parse it.
  • Does HttpClient add the )]}' prefix to request bodies it sends?
    No. The prefix protects responses from being executed as scripts on another site, so only the server emits it. `HttpClient` merely recognises and removes it on the way in.

The prefix is like opening every letter with a deliberately garbled first line: a stranger who tries to read it aloud stumbles and stops at once, while your own assistant knows to skip that line and read the rest.

saying these in an interview costs you the question

  • HttpClient adds the )]}' prefix to protect the app's own responses.
  • The prefix is a way to authenticate that JSON came from your server.
  • XSSI and XSRF are the same attack with different names.
  • The prefix is stripped for every response type, including text.
  • The prefix encrypts or hides the JSON from network observers.