Skip to content

CORS error when vite attempts to preload dynamically imported css in built application #14678

@brettburley

Description

@brettburley

Reproduction

Go to https://stackblitz.com/edit/github-gccjk6nw?file=package.json which should run npm run build && npm run start and view network calls for css-component-jBA8ckt5.css and you will see a mix of fetching of the same asset from react-router tags and vite tags, which are using different variants of the crossorigin attribute.

Image Image

However, you will not see the complete failure as reported below in Stackblitz because the assets are always returned via serviceworker and not via CDN with CORS + browser file cache.

System Info

System:
    OS: Linux 5.0 undefined
    CPU: (8) x64 Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
    Memory: 0 Bytes / 0 Bytes
    Shell: 1.0 - /bin/jsh
  Binaries:
    Node: 20.19.1 - /usr/local/bin/node
    Yarn: 1.22.19 - /usr/local/bin/yarn
    npm: 10.8.2 - /usr/local/bin/npm
    pnpm: 8.15.6 - /usr/local/bin/pnpm
  npmPackages:
    @react-router/dev: 7.10.1 => 7.10.1 
    @react-router/node: 7.10.1 => 7.10.1 
    @react-router/serve: 7.10.1 => 7.10.1 
    react-router: 7.10.1 => 7.10.1 
    vite: ^7.1.7 => 7.3.0

Used Package Manager

npm

Expected Behavior

I expect the asset to be fetched only once, or with the same crossorigin value so that when the browser re-uses cached assets, one of the calls does not unexpectedly fail.

Actual Behavior

When using a CDN like AWS cloudfront with access-control-allow-origin headers, these CDNs will not return any CORS headers when an asset is fetched via a <link /> tag with no crossorigin attributes. However, Chrome will then cache this response, and try to re-use it when vite is later trying to preload the asset, which Chrome will then later fail because the cached version of the asset is missing the access-control-allow-origin header.

After the implementation of the fix in #14463, Chrome is re-using the cache for the two requests, and vite is also trying to fetch the asset since it doesn't find a matching <link /> tag in the DOM as designed due to the # suffix. As a result, Chrome re-uses the cache for both requests, but the former one may not have any CORS response headers due to no crossorigin attribute on the react-router injected tags.

Image

This previously worked in versions of react-router prior to #14463 because vite would not re-attempt to fetch the css asset since it was already found in the DOM.

This article does a good job explaining Chrome's cache usage in combination with CORS headers in more detail: https://anthonymineo.com/solving-a-cors-issue-when-serving-from-chromes-browser-cache/

And the Chromium issue that describes this caching behavior in more detail: https://issues.chromium.org/issues/40381978

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions