CW CleanWebTools
Tools Guides About Privacy Open a tool
Guides / Formatting JSON Client-Side: Why It Matters for Secrets

Formatting JSON Client-Side: Why It Matters for Secrets

By Alpha Loop · Published June 12, 2026 · Updated June 20, 2026 · 7 min read

What is actually inside the response you just pasted

Open the network tab on any authenticated app and copy one response body. Before it is a tidy tree, it is a wall of minified text. Mine, from a billing dashboard I was debugging last month, looked like this:

{"user":{"id":1071004095431376898,"email":"finance@acme-internal.co","role":"owner"},"session":{"access_token":"eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI0Mi...","refresh_token":"v1.MgT8...","expires":1718900000},"webhook_url":"https://hooks.acme-internal.net/ingest/9f2c"}}```

Count what is in there. A user email tied to a real person. An owner-role flag. A bearer token that, until it expires, is the user. A refresh token that outlives the access token. An internal webhook URL that maps your private infrastructure. None of that is exotic. That is a boringly typical payload, and it is the kind of thing developers paste into a formatter forty times a day without a second thought.

The habit is harmless right up until the formatter is not running on your machine.

## Where "format my JSON" quietly becomes "upload my JSON"

A server-side formatter has a simple flow: your text goes into an HTTP request body, travels to someone else's machine, gets prettified there, and comes back. The pretty output is the product. The raw input is the byproduct, and the byproduct is sitting in the request logs of a server you do not control.

I learned to care about this the unglamorous way. Early in a contract I pasted a staging response into the first "JSON beautifier" a search returned, fixed the bug, and moved on. Weeks later, during a security review, someone asked where a particular staging token had been seen outside our systems. The honest answer was that I had handed it to an anonymous web service whose retention policy I never read, whose TLS I never inspected, and whose owner I could not name. The token was low-value and already rotated, so nothing burned. But I had no way to *prove* nothing burned, and in a review that distinction is the whole game. "It was probably fine" is not a sentence you want to say out loud to an auditor.

That is the entire argument for client-side processing, and it is almost embarrassingly simple. If the formatting happens in your browser tab with JavaScript, the text never leaves the machine. There is no request body, no server log, no retention question, no third party. You can confirm it yourself: open the network panel, format a payload, and watch for outbound requests. A correct [client-side JSON formatter](/tools/json-formatter/) produces exactly zero. The same logic is why I will only ever inspect token claims in a [local JWT decoder](/tools/jwt-decoder/) — a JWT is a credential in transit, and pasting one into a remote box is pasting a key into a stranger's pocket.

The privacy case is the easy half. The half that bites people who have already adopted local tools is technical, and it hides inside a single number.

## The pitfall: 9007199254740991

JavaScript has exactly one numeric type for the values you meet day to day, an IEEE 754 double. It represents integers exactly only up to `Number.MAX_SAFE_INTEGER`, which is `2^53 − 1`, or `9007199254740991`. Past that line, the gaps between representable integers grow larger than one, and the values that fall into the cracks get rounded to the nearest one that fits. The brutal part is the silence. There is no exception, no warning, no `NaN`. The wrong answer is returned with total confidence.

Here is the failure on the machine I wrote this on, Node v22.17.0, identical to what V8 does in your browser:

```js
const raw = '{"id": 1071004095431376898, "name": "alpha"}';

const obj = JSON.parse(raw);
console.log(obj.id);                 // 1071004095431376900   <-- last digits wrong
console.log(JSON.stringify(obj));    // {"id":1071004095431376900,"name":"alpha"}

// A cleaner demonstration of the boundary:
console.log(JSON.parse('9007199254740993')); // 9007199254740992

Input ...898, output ...900. Input 9007199254740993, output ...992. The integer 9007199254740993 cannot be represented, so the parser hands back the even neighbor 9007199254740992 and tells you nothing went wrong.

That snowflake-looking 1071004095431376898 is not contrived. It is the shape of a Discord or Twitter ID, and it is the reason both platforms transport those IDs as JSON strings rather than numbers. The integer is real and exact; the moment it becomes a JavaScript Number it is corrupted. Anyone who has ever filtered Discord messages by author ID and gotten phantom non-matches has met this bug, usually after an hour of blaming their own logic.

Now connect it back to the formatter. A naive "pretty printer" is two lines:

function badFormat(text) {
  return JSON.stringify(JSON.parse(text), null, 2);  // round-trips through Number
}

That code is a precision shredder. Every oversized integer in the input is silently mangled on the way through, because JSON.parse materialises it as a Number before JSON.stringify ever sees it. You paste a payload to read it more clearly, and the act of reading it changes the data. For an inspection tool, that is the worst possible property: the instrument alters the specimen.

How a string-level formatter sidesteps it

The fix is conceptual, not clever. Formatting is a whitespace problem, not a parsing problem. Per RFC 8259 §2, the whitespace between structural tokens — the space after a colon, the newline after a comma — is insignificant; you may add or remove it freely. Whitespace inside a string is significant and must be preserved byte for byte. Indentation, therefore, never requires turning numbers into Numbers. It only requires walking the raw characters, tracking whether you are inside a string literal, and inserting newlines and indents around the structural punctuation.

A tokenizer that respects that boundary copies 1071004095431376898 through as the literal text 1071004095431376898. No Number, no IEEE 754, no rounding. The digits in equal the digits out. This is the core reason a serious formatter works on text rather than on parsed objects — and as a bonus it can format input that does not even parse cleanly, which is precisely when you most need to read it.

If you do need the value and not just the text, modern runtimes finally offer a real escape hatch. Node 21+ and current browsers expose the source string to a reviver, so you can lift big integers into BigInt losslessly:

const raw = '{"id": 1071004095431376898}';
const obj = JSON.parse(raw, (key, value, ctx) =>
  key === 'id' ? BigInt(ctx.source) : value
);
console.log(obj.id.toString());  // 1071004095431376898  (exact)

That ctx.source carries the original digits before any double ever touched them, which is the only way to recover the true integer after the fact.

Two more sharp edges from the same spec

While we are in RFC 8259, two rules trip people constantly. Trailing commas are illegal — [1, 2, 3,] is not valid JSON, no matter how reasonable it looks coming from a JavaScript array literal. Comments are illegal too; there is no // or /* */ in the grammar, which is why config formats like JSON5 and JSONC exist as separate, non-standard dialects. A strict formatter should reject both rather than guess at your intent, because guessing is how a tool quietly produces output that some other parser will refuse.

The rule I keep

Treat every JSON blob as if it contains a live credential, because statistically it does, and treat every formatter as if it might both leak your data and corrupt your integers, because the careless ones do exactly that. Run the formatting where the data already lives — your own tab — and use a tool that manipulates text rather than round-tripping through JSON.parse. The browser-local formatter on this site is built on that second principle, and when the payload is a token rather than a tree, the JWT decoder follows the same one: your secrets stay on your hardware, and your ...898 stays ...898.

Tools used in this guide

  • JSON Formatter — Paste JSON, validate it, format it with indentation, or minify it into compact output for APIs and config files.
  • JWT Decoder — Paste a JSON Web Token and inspect its header, payload, and signature segment locally in your browser.
CleanWebTools
Free browser tools that run locally.
Tools Guides About Privacy Terms Contact