What problem are you trying to solve?
<link> and <script> elements load resources declaratively, driven entirely by markup, before any page script needs to run. The Cross-Origin Storage (COS) API (formal spec draft) is a proposed browser mechanism that lets resources like AI models, Wasm modules, popular JavaScript libraries, or Web fonts, be stored once and retrieved across origins, identified by their cryptographic hash rather than by URL. Today, the only way to opt a resource into COS is the (proposed) imperative navigator.crossOriginStorage.requestFileHandle() API, which has no bearing on <link>/<script> at all: a page that wants a stylesheet or script to participate in COS has to abandon markup-only loading entirely and reimplement the fetch, hash lookup, and insertion in script, losing the parser's native preloading and prioritization in the process.
This issue proposes closing that gap with a crossoriginstorage content attribute on <link> and <script>, so COS participation can be declared in markup the same way Subresource Integrity already is. A parallel CSS Working Group proposal (w3c/csswg-drafts#14056) and a companion whatwg/html issue for a crossOriginStorage import attribute share the same underlying model (see "Anything else?" below).
What solutions exist today?
The only existing (proposed) opt-in is the imperative navigator.crossOriginStorage.requestFileHandle() API. It works, but only if the page is willing to give up declarative loading for the resource entirely: instead of a plain <link>/<script> with an integrity attribute, the page has to call requestFileHandle(), read the resulting handle into a Blob, create an object URL, and point the element at that, all before the resource can load. That's extra code for every resource, it delays loading until the script that does this has run, and it does not help at all for a <script> needed at parse time, before any page script has executed.
How would you solve it?
Add a crossoriginstorage content attribute to <link> and <script>. The attribute is only meaningful when integrity is also present; integrity provides the hash that identifies the resource in COS.
The attribute value mirrors the origins option of requestFileHandle():
| Value |
Meaning |
| absent |
No COS participation (today's behavior) |
| empty / valueless |
Same-site access only |
"*" |
Globally available to all origins |
| Space-separated origin list |
Available to the listed origins only |
A space-separated origin list is subject to the same implementation-defined maximum length as the origins option of requestFileHandle(). Without a cap, this attribute would let a page enumerate enough origins to functionally approximate "*" without ever using it, which is exactly the bypass the imperative API's cap exists to prevent (see Cross-site probing). Unlike the imperative API, a content attribute has no exception channel to reject through; an oversized (or otherwise malformed) list should most likely be treated as if crossoriginstorage were absent, rather than failing the element's fetch outright, but this is an open question (see "Anything else?" below).
Examples
Same-site only
A valueless attribute opts the resource into COS for same-site access only:
<link
rel="stylesheet"
href="same-site-css-framework.css"
integrity="sha256-abc123..."
crossoriginstorage
/>
<script
src="same-site-js-framework.js"
integrity="sha256-def456..."
crossoriginstorage
></script>
Globally available
<link
rel="stylesheet"
href="popular-css-framework.css"
integrity="sha256-abc123..."
crossoriginstorage="*"
/>
<script
src="popular-js-framework.js"
integrity="sha256-def456..."
crossoriginstorage="*"
></script>
Restricted to specific origins
<script
src="acme-inc-corporate.js"
integrity="sha256-def456..."
crossoriginstorage="https://acme-inc.example.com https://acme-cdn.example.com"
></script>
No COS (today's behavior preserved)
Omitting crossoriginstorage entirely while keeping integrity preserves existing SRI behavior: the resource is fetched and the hash is verified, but COS is never consulted.
<script
src="popular-js-framework.js"
integrity="sha256-def456..."
></script>
Processing model
- If
crossoriginstorage is absent, proceed with the existing fetch and optional SRI check. COS is not involved.
- If
crossoriginstorage is present, before fetching from the network, the user agent checks COS for a resource matching the integrity hash. If found and the requesting origin satisfies the declared crossoriginstorage value, the resource is served from COS; no network request is made.
- If no COS hit, the resource is fetched from the network as usual. If the fetched content matches the
integrity hash and the origin is permitted by crossoriginstorage, the user agent stores the resource in COS for future use. If the hash does not match, the resource is rejected per existing SRI behavior and is not stored in COS.
Step 2's "found" is not just presence-in-COS: even when the requesting origin satisfies crossoriginstorage, the lookup can still miss for privacy reasons: the hash may not be on the Public Hash List, or the user agent may apply GREASE'ing. This is indistinguishable from a genuine cache miss and falls through to step 3 exactly the same way; see Availability gating.
Progressive enhancement
This is naturally progressively enhancing, verified directly: an unrecognized crossoriginstorage attribute is inert content-attribute data to a browser that doesn't understand it. It reflects as a plain attribute (no IDL exposure), triggers no console warning, and has no effect on the element's normal href/src-driven fetch: the <link>/<script> loads exactly as if crossoriginstorage were never written. No feature detection, fallback markup, or authoring discipline required.
Relationship to existing attributes
integrity: crossoriginstorage depends on integrity being present. Without a hash there is nothing to look up in COS. User agents should ignore crossoriginstorage when integrity is absent.
- Note on
crossorigin: Entirely orthogonal. crossorigin controls the CORS request mode for the network fetch; crossoriginstorage controls COS participation.
Anything else?
Open questions:
- Should
crossoriginstorage be silently ignored or trigger a warning when integrity is absent? (Authors' hunch: trigger a warning.)
- Should the attribute be valid only on specific
rel values for <link> (e.g., stylesheet, preload), or on all <link> elements? (Authors' hunch: start with stylesheet, preload, and modulepreload.)
- What should happen when
crossoriginstorage is present but the cross-origin-storage Permissions Policy feature disallows the current context? The imperative API rejects with NotAllowedError; this attribute has no exception channel, so the likely answer is to silently behave as if crossoriginstorage were absent (fetch from the network, never touch COS), consistent with an ordinary COS miss. (Authors' hunch: behave as if crossoriginstorage were absent.)
- Similarly, what should happen when the
crossoriginstorage value itself is malformed, for example, an origin list exceeding the maximum length, or a string that fails to parse as an origin? Silently treating the element as if crossoriginstorage were absent seems most consistent with question 3's likely answer, but this should also be confirmed explicitly rather than left implicit. (Authors' hunch: behave as if crossoriginstorage were absent.)
Related proposals
Related proposals, sharing the same underlying model:
What problem are you trying to solve?
<link>and<script>elements load resources declaratively, driven entirely by markup, before any page script needs to run. The Cross-Origin Storage (COS) API (formal spec draft) is a proposed browser mechanism that lets resources like AI models, Wasm modules, popular JavaScript libraries, or Web fonts, be stored once and retrieved across origins, identified by their cryptographic hash rather than by URL. Today, the only way to opt a resource into COS is the (proposed) imperativenavigator.crossOriginStorage.requestFileHandle()API, which has no bearing on<link>/<script>at all: a page that wants a stylesheet or script to participate in COS has to abandon markup-only loading entirely and reimplement the fetch, hash lookup, and insertion in script, losing the parser's native preloading and prioritization in the process.This issue proposes closing that gap with a
crossoriginstoragecontent attribute on<link>and<script>, so COS participation can be declared in markup the same way Subresource Integrity already is. A parallel CSS Working Group proposal (w3c/csswg-drafts#14056) and a companion whatwg/html issue for acrossOriginStorageimport attribute share the same underlying model (see "Anything else?" below).What solutions exist today?
The only existing (proposed) opt-in is the imperative
navigator.crossOriginStorage.requestFileHandle()API. It works, but only if the page is willing to give up declarative loading for the resource entirely: instead of a plain<link>/<script>with anintegrityattribute, the page has to callrequestFileHandle(), read the resulting handle into aBlob, create an object URL, and point the element at that, all before the resource can load. That's extra code for every resource, it delays loading until the script that does this has run, and it does not help at all for a<script>needed at parse time, before any page script has executed.How would you solve it?
Add a
crossoriginstoragecontent attribute to<link>and<script>. The attribute is only meaningful whenintegrityis also present;integrityprovides the hash that identifies the resource in COS.The attribute value mirrors the
originsoption ofrequestFileHandle():"*"A space-separated origin list is subject to the same implementation-defined maximum length as the
originsoption ofrequestFileHandle(). Without a cap, this attribute would let a page enumerate enough origins to functionally approximate"*"without ever using it, which is exactly the bypass the imperative API's cap exists to prevent (see Cross-site probing). Unlike the imperative API, a content attribute has no exception channel to reject through; an oversized (or otherwise malformed) list should most likely be treated as ifcrossoriginstoragewere absent, rather than failing the element's fetch outright, but this is an open question (see "Anything else?" below).Examples
Same-site only
A valueless attribute opts the resource into COS for same-site access only:
Globally available
Restricted to specific origins
No COS (today's behavior preserved)
Omitting
crossoriginstorageentirely while keepingintegritypreserves existing SRI behavior: the resource is fetched and the hash is verified, but COS is never consulted.Processing model
crossoriginstorageis absent, proceed with the existing fetch and optional SRI check. COS is not involved.crossoriginstorageis present, before fetching from the network, the user agent checks COS for a resource matching theintegrityhash. If found and the requesting origin satisfies the declaredcrossoriginstoragevalue, the resource is served from COS; no network request is made.integrityhash and the origin is permitted bycrossoriginstorage, the user agent stores the resource in COS for future use. If the hash does not match, the resource is rejected per existing SRI behavior and is not stored in COS.Step 2's "found" is not just presence-in-COS: even when the requesting origin satisfies
crossoriginstorage, the lookup can still miss for privacy reasons: the hash may not be on the Public Hash List, or the user agent may apply GREASE'ing. This is indistinguishable from a genuine cache miss and falls through to step 3 exactly the same way; see Availability gating.Progressive enhancement
This is naturally progressively enhancing, verified directly: an unrecognized
crossoriginstorageattribute is inert content-attribute data to a browser that doesn't understand it. It reflects as a plain attribute (no IDL exposure), triggers no console warning, and has no effect on the element's normalhref/src-driven fetch: the<link>/<script>loads exactly as ifcrossoriginstoragewere never written. No feature detection, fallback markup, or authoring discipline required.Relationship to existing attributes
integrity:crossoriginstoragedepends onintegritybeing present. Without a hash there is nothing to look up in COS. User agents should ignorecrossoriginstoragewhenintegrityis absent.crossorigin: Entirely orthogonal.crossorigincontrols the CORS request mode for the network fetch;crossoriginstoragecontrols COS participation.Anything else?
Open questions:
crossoriginstoragebe silently ignored or trigger a warning whenintegrityis absent? (Authors' hunch: trigger a warning.)relvalues for<link>(e.g.,stylesheet,preload), or on all<link>elements? (Authors' hunch: start withstylesheet,preload, andmodulepreload.)crossoriginstorageis present but thecross-origin-storagePermissions Policy feature disallows the current context? The imperative API rejects withNotAllowedError; this attribute has no exception channel, so the likely answer is to silently behave as ifcrossoriginstoragewere absent (fetch from the network, never touch COS), consistent with an ordinary COS miss. (Authors' hunch: behave as ifcrossoriginstoragewere absent.)crossoriginstoragevalue itself is malformed, for example, an origin list exceeding the maximum length, or a string that fails to parse as an origin? Silently treating the element as ifcrossoriginstoragewere absent seems most consistent with question 3's likely answer, but this should also be confirmed explicitly rather than left implicit. (Authors' hunch: behave as ifcrossoriginstoragewere absent.)Related proposals
Related proposals, sharing the same underlying model:
cross-origin-storage()<request-url-modifier>.crossOriginStorageimport attribute (COS integration) #12771 for acrossOriginStorageimport attribute, covering staticimportand dynamicimport().