Deployment
Published
A report arrives saying your application does not load, and it is only ever from one customer. Every user inside that company sees it, nobody outside does, the browser versions are ordinary and the same build works everywhere else on earth. Before rereading your own diff, it is worth knowing what a large organisation typically has sitting between your server and its employees, because that appliance is a far more likely explanation than anything in your build.
The bundle does not travel untouched
Most reasoning about a deployed bundle assumes a clean path: your origin serves bytes, a CDN caches them, a browser executes them. In consumer traffic that is close enough to true. In a large enterprise it frequently is not, and the difference explains a category of bug reports that are otherwise baffling because they reproduce for exactly one customer and for every single person inside it.
What sits in between is an inspecting proxy. The organisation installs its own certificate authority onto managed devices, which lets an appliance terminate the TLS connection, decrypt the traffic, apply whatever policy it has been configured with, then re-encrypt with its own certificate and hand the result to the browser. The browser is satisfied because it trusts the internal authority. From your side nothing is visibly different. From the appliance's side your JavaScript is a plaintext file it is entitled to read, log, score, and in some configurations modify.
First consequence: the transport is not private from the enterprise
This is worth stating plainly because it quietly undermines a common argument. Teams sometimes justify shipping something sensitive in a bundle on the grounds that it only travels over HTTPS to authenticated users. Inside an inspecting enterprise that reasoning does not survive, because the bundle is in the clear within the appliance and frequently retained in its logs.
None of this is an attack. It is a disclosed, deliberate control that the organisation runs on its own managed devices for its own reasons. But it does add one more location to the list of places your client-side code exists in readable form, alongside the browser itself and the developer tools of every user, and it reinforces the conclusion the rest of this site keeps arriving at from other directions: a bundle is a published artefact, and the only durable answer for a genuine secret is that it never goes in one.
Second consequence: heuristic scoring, a different device, the same trigger
Many of these appliances do more than watch. Web filtering and data-loss modules score scripts on surface characteristics, and the traits they weight are recognisable: high character entropy, unusually long string tables, deeply nested expressions, dynamically constructed calls. A protected bundle is denser in most of those than the source it came from, so it can score higher than the same application unprotected.
The important framing is that this is the same family of heuristic that produces endpoint antivirus alerts, applied by a different device at a different point on the path. They are separate systems with separate vendors and separate submission processes, and confusing them costs a support cycle. The endpoint case, including how to submit a false positive to a vendor, is covered separately; this article is about the network device.
Third consequence: modification breaks integrity checks
Some appliances do not merely inspect, they alter. Recompression, whitespace normalisation, URL rewriting to route through the proxy, and injected notice banners all occur in real deployments. Any of them changes the bytes.
If you use Subresource Integrity, that is immediately fatal in a specific and diagnosable way. An SRI hash covers exact bytes, so a modified response fails the check and the browser declines to execute the script at all. The console message talks about integrity, which points the investigation at your build rather than at the network, and teams can lose a day to that misdirection. It is worth distinguishing this from the build-order mistake of hashing before the transform runs, which produces a similar-looking failure for a completely different reason and is handled in its own article. The tell is simple: a build-order error fails everywhere, and a proxy modification fails on one network.
How to diagnose it in about an hour
The whole investigation reduces to comparing bytes, and the record you need was created by your build. Ask someone on the affected network to save the script exactly as their browser received it, using the network panel rather than a fresh fetch through a different path, and to hash the saved file. Compare that digest against the output digest recorded in the manifest for that release.
If the digests match, the file arrived intact and the problem is elsewhere, which is worth knowing quickly because it eliminates the entire network theory in one step. If they differ, the bytes changed between your origin and that browser, and the conversation shifts to their network team with an artefact and two hashes attached rather than a hypothesis. A comparison of the two files usually shows immediately what was done, whether that is recompression, a rewritten URL or an injected banner.
Two smaller signals are worth collecting at the same time. Whether the response headers the browser sees match the ones you set, since appliances commonly rewrite caching and content-encoding headers, and whether the certificate the browser reports for your domain was issued by a public authority or an internal one. The second answers whether inspection is happening at all, and it takes about ten seconds.
What to change, and what is not worth changing
The honest position is that most of the fix belongs to the customer, and your goal is to make granting it easy. A few things on your side genuinely help.
Keep a readable banner at the very top of the artefact naming the product, the version and a contact URL. This is worth knowing precisely: the header comment feature preserves the leading run of comments, stopping at the first statement, so a banner placed above all code survives while a per-dependency notice sitting further down does not. That single block is what gives an appliance operator, and the human reading its log, something to identify rather than an anonymous wall of dense script.
Serve from a stable hostname, because allowlists are written against hostnames and rotating domains make an entry impossible to maintain. Publish a short page describing what your bundle is and why it looks the way it does, so a customer's security team has something to attach to a change request. And keep the release digests, since that is what converts the next instance of this from a debate into a five-minute check.
What is not worth doing is weakening your protection settings in the hope of scoring lower on an opaque heuristic you cannot see, tune or test against. The scoring differs by vendor and version, you get no feedback signal, and you would be trading a real property for a guess. The productive path is the allowlist entry, and the material above is what gets it granted.
The short version
In enterprise networks your bundle is decrypted, inspected, sometimes scored and occasionally modified by an appliance you do not control. That means the transport is not private from the enterprise, a protected file can trip heuristics that its source would not, and any modification breaks an integrity check outright. Diagnose it by hashing what the browser actually received and comparing against your manifest. Fix it by making your artefact identifiable and your allowlist request easy to approve, not by making your protection weaker.
Frequently asked questions
Why does our protected bundle only break for one customer?
Because the thing that is different is their network rather than their browser. A large organisation frequently runs an inspecting proxy that terminates TLS internally, examines what passes through, and re-originates the connection to the browser. That appliance sits between your server and your user, and it is configured by a team you have never spoken to. When breakage is confined to one company and reproduces for every user inside it while working everywhere else, the network path is the first thing to suspect and it is usually the answer.
Does an inspecting proxy mean HTTPS is not protecting our bundle?
Inside that organisation, not from the organisation itself. TLS inspection works because the enterprise installs its own certificate authority on managed devices, so the appliance can present a certificate the browser trusts, decrypt, inspect, and re-encrypt. Your bundle exists in readable form inside that appliance and often in its logs. This is a deliberate, disclosed security control rather than an attack, and the practical implication is that any argument for shipping something sensitive in a bundle which rested on the transport being private does not hold in an enterprise network.
Can a proxy break Subresource Integrity?
Yes, and it is one of the clearer symptoms. An SRI hash covers exact bytes, so any appliance that recompresses, rewrites URLs, injects a banner, or normalises whitespace produces a file whose digest no longer matches the attribute and the browser refuses to execute it. The failure is total and silent in the sense that the console message points at integrity rather than at the proxy. If this reproduces on one network only, the appliance is modifying the response and no change to your build will make the hash match again.
Is a protected bundle more likely to be flagged by a web filter?
It can be, because some filters score scripts on surface characteristics such as high entropy, long string tables and dynamically constructed calls, and those are exactly the traits a transform introduces. This is the same family of heuristic that produces endpoint antivirus alerts, applied by a different device on a different part of the path. It is worth knowing that the two are separate systems even though the trigger overlaps, and the endpoint antivirus case has its own write-up covering the vendor submission process.
How do we prove the file was altered in transit?
Compare digests, which turns an argument into a measurement. Have someone on the affected network save the script exactly as their browser received it and hash it, then compare that against the output digest your build recorded for that release. If they differ, the bytes changed between your origin and that browser and the conversation moves to their network team with evidence attached. This is one of the more useful everyday applications of keeping a build manifest, since the record you need already exists.
What can we change on our side to reduce this?
Less than you would like, and a few things genuinely help. Keep an intelligible comment banner at the top of the artefact naming the product, version and a contact URL, since the leading run of comments is preserved and it gives both an appliance and a human reviewer something to identify. Serve from a stable hostname rather than rotating domains. Publish a short page your customer's security team can point at when requesting an allowlist entry. Beyond that, the fix is an allowlist on their side, and making that easy to grant is the practical goal.
Related reading