Sometimes a key or token has to be in the client bundle because the browser makes the call directly. Obfuscation changes one part of that problem and not the rest.
A readable key is found by bots, not by people
A key sitting in shipped JavaScript as readable text is one grep away from anyone looking. Crawling secret scanners find it without a human ever opening your site, and that automated discovery is a common path from a shipped key to an abused one.
Nobody has to be interested in your product for this to happen. A bot checks every public bundle it can reach for a string shaped like a credential, and yours is in the queue with the rest.
What encoding the key buys, and where it stops
strings.encode is on at any positive complexity target, so string literals in your source are encoded in the output instead of shipped as text. A scanner that greps the bundle for credential-shaped strings does not find the key that way.
That is worth having, and it is all it is. Obfuscation hides how a value gets used. It does not hide the value: anything the browser needs in order to run your code can be reached by running that code. A sealed key is not a safe key.
Restrict, proxy, rotate, then obfuscate
Do these four things, in order, and treat obfuscation as the last one:
- Restrict the key at the provider to the narrowest scope, origins and quota it offers.
- Move the authority behind your own endpoint, so the browser never holds the real credential.
- Rotate the key that already shipped. Assume it is compromised from the moment it went out.
- Obfuscate whatever genuinely cannot move server-side.
For that last step the default build already encodes it. To set a build gate for CI:
{
"strings": {
"leaks": {
"max": 0
}
}
}A directive raises one value above the rest of the program, on Pro builds.
const apiKey = /* @afterpack preset=extreme */ "YOUR_SECRET";Fail the build on a surviving literal
--strings.leaks.max=0 fails the build the moment a readable literal survives, naming DIAG_SURVIVING_READABLE_LITERAL. Wire that into CI so it is not a check you do by eye. To see what a deployed site exposes right now, run npx afterpack@latest audit against it.
A shipped key is still a shipped key
Encoding raises the cost of finding the key by reading the shipped file, and a build that survives the leak gate is a real result. It is not evidence that the key is out of reach: the value is still in the output, encoded, and reconstructed at the moment your code uses it, so anyone running the page in a browser they control can read it back. Rotating the key and moving the authority to your own server are the steps that close the exposure.
Next
- FAQ: the short answer to this exact question.
- Threat model: the boundary between a static reader and someone running your code.
- Best practices: the rotation steps if a key already went out.