Under Cypress, why does the browser block a first-party script carrying an `integrity` attribute?
answer
- Something changed the file in flight
- A default-on option does the rewriting
- Hashes are computed over exact bytes
- One option strips the pinned attribute
- First-party and third-party have separate switches
basics
~20 sCypress rewrites obstructive framebusting code in served HTML and JS by default, which changes the file's bytes. The pinned Subresource Integrity hash no longer matches, so the browser refuses the resource. Setting removeSRIAttributes to true strips the attribute.
solid answer
~40 sBecause Cypress rewrote it. `modifyObstructiveCode` defaults to `true` and neutralises framebusting patterns such as `top === self`, `top.location` and `<base target="_top">` in served `.html` and `.js`. Rewriting changes the resource's bytes, so a pinned Subresource Integrity hash stops matching and the browser blocks the file, reporting `Failed to find a valid digest in the 'integrity' attribute for resource '…'`. The bundle never runs, so the page renders empty and the first assertion times out on an element that was never built. Set `removeSRIAttributes: true` and Cypress strips the `integrity` attribute from `<script>` and `<link>` elements as they are served. It covers first-party resources, including `integrity` set as a static attribute, a string literal, or a runtime DOM property assignment; `experimentalModifyObstructiveThirdPartyCode` is the third-party counterpart.
code
javascript · 13 lines// cypress.config.js
module.exports = {
// default: Cypress rewrites framebusting code in .html and .js
modifyObstructiveCode: true,
// strip integrity from first-party <script> and <link> so the
// rewritten statements bundle is not blocked by the browser
removeSRIAttributes: true,
// the third-party counterpart, for the embedded PDF viewer's scripts
experimentalModifyObstructiveThirdPartyCode: true,
e2e: {
baseUrl: 'http://localhost:3000',
},
}go deeper
Know that Cypress may modify HTML and JavaScript as it is served so the app can run inside a frame, and that this is a default rather than something you switched on.
Explain the causal chain: rewriting changes bytes, changed bytes break a Subresource Integrity hash, the browser blocks the resource. Name the option that strips the attribute and its default.
Show the diagnosis. The visible failure is a timeout on a missing element; be ready to say how you would get from that to the browser console error and then to the right configuration option.
Weigh the trade: stripping SRI under test disables a tampering check, and widening it to third-party code widens the blast radius. Decide whether the app should framebust at all rather than configuring around it forever.
## What Cypress rewrites, and why Cypress runs your spec in the same browser as the application under test, with the app loaded in a frame. Plenty of production code actively resists that: **framebusting** and clickjacking defences compare `top` with `self`, assign to `top.window.location`, or set `<base target="_top">` so that every untargeted link escapes the frame. Those techniques predate modern browser defences and would break the run. `modifyObstructiveCode` — **on by default** — makes Cypress scan `.html` and `.js` responses as they are served and neutralise those patterns. In practice it rewrites things like: - `window.top === window.self` comparisons, so the check no longer detects the frame - `top.location` and `parent.frames` references, redirected to `self` - outright busts such as `top.window.location = '…'` - a `<base target="_top">` or `_parent` attribute, stripped so navigation stays in the frame There is a small performance cost to scanning response streams, and the patterns are regular expressions, so on rare occasions they can rewrite valid code. Both are documented reasons to turn the option off for an app that does not framebust. ## Why the integrity hash then fails **Subresource Integrity** is a browser feature: you publish a hash of a file in the element that loads it, and the browser refuses the resource if the bytes it received do not hash to that value. ```html <script src="/assets/statements.js" integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC" crossorigin="anonymous"></script> ``` Cypress's rewriting **changes the bytes**. The hash was computed over the original file, the browser hashes what actually arrived, the two do not match, and the browser blocks the resource with an error like: ``` Failed to find a valid digest in the 'integrity' attribute for resource '…'. The resource has been blocked. ``` The symptom is confusing because nothing in your test is wrong: the bank-statement bundle simply never executes, so the page renders empty and the first `cy.get()` times out on an element that was never built. ## The fix Set **`removeSRIAttributes: true`**. Cypress then strips the `integrity` attribute from `<script>` and `<link>` elements as they are served, so a rewritten resource loads normally. It covers the attribute however it is expressed: 1. a static HTML attribute, `<script src="app.js" integrity="sha384-…">` 2. a JavaScript string literal assigned to the attribute 3. a runtime DOM property assignment, such as a bundler plugin doing `script.integrity = sriHashes[chunkId]` It defaults to `false`, so nothing changes for the majority of applications that do not pin subresources. Enable it only when you are actually seeing first-party resources blocked, and only for the application under test — you are switching off a check that exists to catch tampered files. ## First-party versus third-party The two options draw the line at the origin under test: | Option | Scope | Default | | --- | --- | --- | | `removeSRIAttributes` | first-party resources of the origin under test | `false` | | `experimentalModifyObstructiveThirdPartyCode` | third-party resources, including their SRI | `false` | If the statements page pins its own bundle **and** the embedded third-party PDF viewer pins its scripts, you need both. `experimentalModifyObstructiveThirdPartyCode` also widens the framebusting patterns and adjusts a couple of request headers for third-party content, so it is not simply the same switch aimed elsewhere. ## Getting from the symptom to the cause The failure does not announce itself. A run in a bank-statement suite looks like this: 1. `cy.visit('/statements/2026-08')` completes; the document loaded fine. 2. The first `cy.get('[data-cy=statement-row]')` times out, because the rows were never rendered. 3. Nothing in the Cypress command log points at the network — the request for the bundle succeeded with a `200`. 4. The browser console carries the real message: the digest check failed and the resource was blocked. That fourth step is the one people skip. **A timeout on an element that never existed is usually a resource that never ran**, and the browser console, not the command log, is where a blocked subresource is reported. The same reasoning applies to a stylesheet pinned with `integrity`: the page renders unstyled rather than empty, and a visibility assertion fails for reasons that look nothing like a network problem. ## The removed option people still reach for `experimentalSourceRewriting` — an AST-based rewriter that used to be the answer here — was **removed in Cypress 16**. The default regex rewriter now handles every case it did, without its performance cost. If a config still sets it, Cypress warns and you should delete the line; if you were using it specifically to dodge SRI failures, `removeSRIAttributes` is the replacement.
- Which option covers the third-party PDF viewer's own SRI-pinned scripts?`experimentalModifyObstructiveThirdPartyCode`. `removeSRIAttributes` applies to first-party resources of the origin under test; the experimental option extends obstructive-code modification to third-party `.js` and `.html` and removes SRI from those modified resources. If both your bundle and an embedded viewer pin subresources, enable both.
- What replaced `experimentalSourceRewriting` for this problem?Nothing replaced it as a rewriter — it was removed in Cypress 16 because the default regex-based rewriter handles every case the AST-based one did, without its performance cost. If you were setting it specifically to avoid SRI failures, `removeSRIAttributes` is the replacement. Delete the removed option from the config; Cypress warns while it is still there.
saying these in an interview costs you the question
- Blames the application's CDN or build hash
- Suggests experimentalSourceRewriting, removed in Cypress 16
- Turns off SRI in production to make tests pass
- Thinks removeSRIAttributes also covers third-party resources
- Assumes modifyObstructiveCode is off by default