Compatibility
Published
URL construction is where a surprising number of production bugs live, because percent-encoding has several rules that look interchangeable and are not. Adding a build step to that area is a reasonable thing to be cautious about. We measured the whole surface, including the string table that rewrites every literal in the file, and then looked at the question people usually mean when they ask this one.
Why the string table makes this worth measuring
For most compatibility questions the honest answer is that the transform never goes near the feature. URLs are a partial exception, because a URL in your source is a string literal, and the string transforms genuinely do relocate and rewrite every literal in the file.
So the concern is specific rather than vague: if a base path or a query key came back from the encoded table subtly altered, URL building is exactly where it would surface, and it would surface as a request to the wrong place rather than as an exception.
The sample builds query strings with URLSearchParams, parses and mutates a URL, compares encodeURI against encodeURIComponent, resolves relative and absolute references against a base, reads origin, port and hash, sorts parameters for a stable cache key, and round-trips both Unicode text and embedded JSON. All five configurations produced identical output, including the string-table preset.
The encoding rules that survive intact
The distinctions people get wrong by hand are all preserved. encodeURI leaves the reserved characters that structure a URL alone, so a b&c=d/e?f#g becomes a%20b&c=d/e?f#g. encodeURIComponent escapes them, giving a%20b%26c%3Dd%2Fe%3Ff%23g. Using the first where you meant the second is how a value with an ampersand in it turns into two parameters, and that behaviour is unchanged after protection.
The + ambiguity is intact too, which matters because it is the one that breaks hand-rolled parsers. Inside a query string a + means a space, so new URLSearchParams("x=a+b").get("x") returns a b. decodeURIComponent("a+b") returns a+b, because outside that context it is a literal plus. Both results were identical before and after protection on every configuration.
URLSearchParams keeps its full behaviour: repeated keys accumulate and are read back with getAll, set replaces while append adds, sort produces the stable ordering you want for a cache key, and iteration yields entries in order. Relative resolution against a base is unchanged, including the case where an absolute URL in the first argument correctly wins over the base.
The failure mode is in your code, not in the build
One case in the sample is there to make a point that has nothing to do with obfuscation. Building a query string by concatenation, without encoding, lets a value containing an ampersand inject an extra parameter: a value of a&admin=true becomes a second key in the resulting URL. Built through URLSearchParams, the same value is encoded and stays a single parameter.
That behaviour is identical before and after protection, which is precisely the point. Protection preserves the bug faithfully. If you have a hand-rolled query builder, obfuscating it does not make it safer, and the same is true of every other client-side flaw — the argument set out in obfuscation will not fix a client-side vulnerability.
The one configuration that can break URL code is the same one that can break any code: a member-renaming pattern broad enough to match a built-in name. The rename is name-based rather than type-aware, so a pattern matching toString or get rewrites those call sites and the built-in is no longer reachable. Scope the pattern to names your own objects define.
What people usually mean by this question
A good share of the people searching for this are not really asking whether URL building still works. They are asking whether obfuscating the bundle hides the endpoints it calls. It does not, and it is worth being direct about why.
Your protected bundle still has to make the request. The URL is assembled at run time and handed to the network layer, so anyone can read it in the browser's network panel without touching your code at all. String encoding raises the effort of finding an endpoint by reading the file; it does nothing about watching the traffic, which is easier anyway.
The same reasoning applies to anything travelling in the URL. A token in a query parameter is visible in the network panel, in server logs, in referrer headers and in browser history, and obfuscation changes none of that. That is the argument made at length in you cannot hide an API key in JavaScript, and query parameters are its most common form.
What protection genuinely gives you here is narrower and still useful: the structure of how you build requests — the signing steps, the parameter derivation, the sequence of calls — is much harder to read and reimplement from a protected bundle than from a readable one. Raising the cost of cloning your client is a real benefit. Hiding the destination of a request is not on offer from any client-side tool.
Practical guidance
You can enable the string table on code that builds URLs without a compatibility review. Encoding, parsing, relative resolution and parameter handling all measured identical, including under the transform that rewrites every literal.
Use URLSearchParams rather than string concatenation, for the reason shown above and independently of anything to do with protection. It is also the form that survives every transform without any special consideration, since the keys and values are ordinary strings.
And set expectations correctly with whoever asked. If the goal is that the endpoint should not be callable by someone who is not your application, that is an authentication and authorisation problem on the server, not a bundling problem. Obfuscation is a useful layer for protecting logic; it is not a mechanism for keeping a URL secret.
Frequently asked questions
Does obfuscation break URLSearchParams?
No. We diffed a URL-building sample against protected copies on five configurations covering the ES5 target, the modern target, two identifier-renaming presets and the string table, and every result was identical. That included repeated keys, getAll, set versus append, sort and iteration order.
Does the string table corrupt URLs in my source?
No. This was the specific reason to include the string-table preset in the measurement, since a URL in your source is a string literal and that transform genuinely relocates every literal. The values come back byte-identical and every parsing and encoding result matched the original.
Does encodeURIComponent still behave differently from encodeURI?
Yes, and the distinction is preserved exactly. encodeURI leaves reserved structural characters alone while encodeURIComponent escapes them. The plus-sign ambiguity is also intact: a plus means a space when read through URLSearchParams but stays a literal plus through decodeURIComponent.
Does obfuscating my JavaScript hide my API endpoints?
No. The bundle still has to make the request, so the URL is visible in the browser network panel regardless of how the file is written. String encoding makes an endpoint harder to find by reading the source, but it does not affect anyone watching the traffic, which is the easier approach anyway.
Is a token in a query parameter protected by obfuscation?
No. Anything in a URL appears in the network panel, in server logs, in referrer headers and in browser history, none of which is affected by how the bundle was built. Values that must stay secret cannot live in client-side code or in the URLs it builds.
Can member renaming break URL handling?
Only if the pattern is too broad. Member renaming matches names with a regular expression and is not type-aware, so a pattern matching a built-in name such as get or toString rewrites those call sites and the built-in is no longer reachable. Anchor the pattern and list the names your own objects define.
Related reading