I want to build a lightweight single-page app that people can use locally over the file://
protocol (i.e. simply by downloading the project archive file, unpacking it and double clicking on index.html
), as well as on the web (https://
).
All my dependencies are stored within my project (e.g. in a /js
folder), including the third-party libraries I use (namely, AlpineJs and JSZip).
So far so good, it works in both scenarios (file and https).
I also want to use the integrity
attribute on the <script>
elements, so that users can verify that I did not temper with the code of those libraries. This gives me something like:
<script
src="js/jszip.min.js"
integrity="sha512-XMVd28F1oH/O71fzwBnV7HucLxVwtxf26XV8P4wPk26EDxuGZ91N8bsOttmnomcCD3CS5ZMRL50H0GgOHvegtg=="
crossorigin="anonymous"
referrerpolicy="no-referrer"
></script>
<script
src="js/alpinejs.min.js"
defer
integrity="sha512-FUaEyIgi9bspXaH6hUadCwBLxKwdH7CW24riiOqA5p8hTNR/RCLv9UpAILKwqs2AN5WtKB52CqbiePBei3qjKg=="
crossorigin="anonymous"
referrerpolicy="no-referrer"
></script>
However, as soon as I start using those attributes, the browser throws a CORS error and refuses to load the files, when accessing the app over the file:// protocol:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at file:///.../js/jszip.min.js. (Reason: CORS request not http). [Learn More]
I'm not entirely familiar with the crossorigin
and referrerpolicy
attributes. Reading the doc about them, I didn't find anything that helped me in this regard. I'm don't think they are needed. However if I remove them, I get a different error:
“file:///.../js/alpinejs.min.js” is not eligible for integrity checks since it’s neither CORS-enabled nor same-origin.
Is there a way to set it up so that it works both over https://
and file://
, while keeping the integrity
attribute?
You cannot have the integrity from file protocol due to the CORS needed to verify the integrity.
You can load the script locally without the integrity like this
const isFileProtocol = window.location.protocol === 'file:';
const scripts = [{
src: 'js/jszip.min.js',
integrity: 'sha512-XMVd28F1oH/O71fzwBnV7HucLxVwtxf26XV8P4wPk26EDxuGZ91N8bsOttmnomcCD3CS5ZMRL50H0GgOHvegtg==',
crossorigin: 'anonymous',
referrerpolicy: 'no-referrer',
defer: false,
},
{
src: 'js/alpinejs.min.js',
integrity: 'sha512-FUaEyIgi9bspXaH6hUadCwBLxKwdH7CW24riiOqA5p8hTNR/RCLv9UpAILKwqs2AN5WtKB52CqbiePBei3qjKg==',
crossorigin: 'anonymous',
referrerpolicy: 'no-referrer',
defer: true,
},
];
scripts.forEach(({ src, integrity, crossorigin, referrerpolicy, defer }) => {
const script = document.createElement('script');
script.src = src;
if (!isFileProtocol) {
script.integrity = integrity;
script.crossOrigin = crossorigin;
}
if (referrerpolicy) {
script.referrerpolicy = referrerpolicy;
}
if (defer) {
script.defer = true;
}
document.head.appendChild(script);
});