CLAUDE WATERMARK REMOVER · PRACTICAL TEST
Why does appending percent-encoded text produce percent-25 in the URL?
Appending the raw value %2F serializes it as value=%252F and reparses to %2F. Appending / serializes as value=%2F and reparses to /. The append API receives value text; parsing a query string is a different input path.
Open the Claude text cleaner · Full measured data
Measured inputs and outputs
These are locally constructed test strings, not evidence that Claude inserts these characters. We executed the saved homepage script snapshot with a minimal DOM harness and compared exact strings. The invisible-checkbox state and dash mode appear in each row. The observations below test this page's specific question. Destination observations run independently on the exact input and cleaned strings in the recorded Node runtime. We make no detector-score or statistical-watermark removal claim.
| Fixture and mode | Input string and code points | Output and cleaner status | Observed before / after |
|---|---|---|---|
| Marked escape spelling direct string / keep / invisible true | "%2F"U+0025 U+0032 U+200B U+0046 | "%2F"Removed 1 invisible character | {"serialized":"value=%252%E2%80%8BF","reparsedValue":"%2F","constructedValue":"%2F"}{"serialized":"value=%252F","reparsedValue":"%2F","constructedValue":"/"} |
| Already escaped slash direct string / keep / invisible true | "%2F"U+0025 U+0032 U+0046 | "%2F"No selected invisible characters found | {"serialized":"value=%252F","reparsedValue":"%2F","constructedValue":"/"}{"serialized":"value=%252F","reparsedValue":"%2F","constructedValue":"/"} |
| Raw slash direct string / keep / invisible true | "/"U+002F | "/"No selected invisible characters found | {"serialized":"value=%2F","reparsedValue":"/","constructedValue":"/"}{"serialized":"value=%2F","reparsedValue":"/","constructedValue":"/"} |
| Raw plus direct string / keep / invisible true | "+"U+002B | "+"No selected invisible characters found | {"serialized":"value=%2B","reparsedValue":"+","constructedValue":" "}{"serialized":"value=%2B","reparsedValue":"+","constructedValue":" "} |
Two inputs that look related carry different value text
The repaired first fixture spells %2F after removal of its actual U+200B interruption. Passing that value to append produces value=%252F; reparsing the serialized parameter string yields the original literal %2F, not a slash. The plain escaped-slash control repeats exactly the same behavior without cleanup. When the supplied value is a literal slash instead, append serializes value=%2F and a single reparse yields a slash. Both paths preserve the raw argument that append received. The percent-25 sequence therefore records an encoded percent character, rather than evidence that the cleaner inserted a character or that the URL serialization failed.
The string constructor also applies form decoding
We additionally pass value= followed by each fixture directly to the URLSearchParams string constructor. For %2F that constructor yields a slash, which differs from append followed by serialization and reparse. The plus control makes the distinction visible another way: append preserves a literal plus through %2B, while the constructed query text interprets the plus as a space. The earlier plus-decoding page studies that decoding rule; this matrix centers on the caller accidentally passing serialized text to a raw-value API. It performs no repeated decoder loop, navigation or server request, and it does not establish how a particular backend handles double-encoded inputs.
Keep the boundary between data and serialized query text explicit
Pass the intended raw value to append and let the parameter serializer encode it once for transport. If an upstream source supplies an already encoded component, first confirm that documented representation and its decoding rules rather than guessing from a percent sign. Some applications intentionally want the literal text %2F, so decoding it blindly would change their data. Our marked escape demonstrates that character repair and representation choice are separate decisions. Save the input and inspect the value obtained after one intended parse at the destination boundary. The evidence downloads preserve both construction paths so the meaning of percent-25 can be checked without treating every retained escape as failed cleanup.
Reproduce this test
Save reproduce.cjs and tested-app.js in the same folder. Run the command below with Node.js. The harness prints its runtime, script SHA-256 and every measured row. Compare those rows with the original record. Using a newer script or runtime creates a new experiment; retain the version information with your rerun.
node reproduce.cjsReference and next check
WHATWG URLSearchParams append provides the relevant primary definition. The table and fixture analysis are original measurements. For broader inspection, use our Unicode inspector. Read the scope distinction before interpreting cleanup as a watermark result.