-
Notifications
You must be signed in to change notification settings - Fork 49.4k
Description
In Chrome, auto-inserted rel=preload
script resources (via renderToPipeableStream
with "nonce" option) are blocked/not preloaded when the page has a CSP with script-src 'nonce-NONCE'
set up.
React version: Latest Canary, 18.3.0-canary-aef7ce554-20230503
.
As far as I can see, the issue exists since 18.3.0-next-28a574ea8-20221027
.
The issue doesn't exist in the latest stable version, 18.2.0, since auto-inserted rel=preload
link tags were added later.
Context: We’re currently in the progress of updating React to v18 in a rather big codebase, and tried to use a Canary release for the updated hydration behavior as described in this comment.
Steps to reproduce
- Set up a CSP with a nonce, e.g.
Content-Security-Policy: script-src 'nonce-NONCE'
- Use
renderToPipeableStream
with the "nonce" option - Open the app in Chrome
The current behavior
React automatically adds rel=preload
link tags for resources, including scripts. When using a nonce with a script-src 'nonce-NONCE'
Content-Security-Policy, and passing the nonce value to renderToPipeableStream
with the "nonce" option, the corresponding auto-inserted <link rel="preload" as="script">
tags don't receive the nonce.
The script resources aren’t preloaded in Chrome because they’re blocked by the CSP (Firefox and Safari preload correctly).
Content-Security-Policy: script-src 'nonce-CSP_NONCE'
import { renderToPipeableStream } from 'react-dom/server';
const { pipe } = renderToPipeableStream(<App />, {
nonce: CSP_NONCE,
bootstrapScripts: ['/main.js'],
onShellReady() {
response.setHeader('content-type', 'text/html');
pipe(response);
}
});
function App() {
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/styles.css"></link>
<title>My app</title>
</head>
<body>
<script src="/script.js" nonce={CSP_NONCE}></script>
</body>
</html>
);
React streams something like the following:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charSet="utf-8" />
<link rel="preload" as="style" href="/styles.css" />
<link rel="preload" as="script" href="/script.js" />
<link rel="stylesheet" href="/styles.css"></link>
<title>My app</title>
</head>
<body>
<script src="/script.js" nonce="CSP_NONCE"></script>
</body>
</html>
The expected behavior
When using a nonce value with the renderToPipeableStream
"nonce" option, the corresponding auto-inserted <link rel="preload" as="script">
tags include the "nonce" attribute so that the resource can preload in Chrome and is not blocked by a script-src 'nonce-NONCE'
Content-Security-Policy.
Related
- Consider relaxing
prefetch-src
and preloadas
w3c/webappsec-csp#542 - https://bugs.chromium.org/p/chromium/issues/detail?id=1406444
As far as I understood, the updated behavior doesn’t apply for<link rel=preload>
, though (only for<link rel=prefetch>
).