Do CSS URL hashes reflect subresources changes?

Person in shorts with blue hair walking left


CSS can reference other CSS stylesheets via @import, font files via @font-face and image files with background-image, mask-image, border-image et al. When the URL hashes for these resourcess are changed, the hashed URLs of any CSS files referring to them must also change.

The Test

This test runs repeated builds of a CSS stylesheet referencing an image, both of which receive hashes in their URLs. The contents of the SVG image are changed causing it to have a different URL hash in the final build build, which should also update the stylesheet's URL hash.


body {
  background-image: url(bg.svg);

In this example, when bg.svg changes, its hashed URL bg.a1b2c.svg will change. Since the built CSS has to be updated with the new image URL, its hashed URL styles.9z8y7.css should also change.

If the content of bg.svg is changed between two builds, the hash of both output files must change compared to the previous build.



The plugin gulp-rev-all analyzes contained file references, rewrites them and calculates a final hash accordingly.


Plugin authors can use Rollup's emitFile method to add assets-of-assets into the graph. A placeholder hash (based on the content of the asset-of-asset) can be used to ensure changes to the asset-of-asset impacts the hash of the asset.

Extending this to assets-of-assets-of-assets would be a jump in complexity for the plugin author, although a simpler model is planned.

There is also egoist/rollup-plugin-postcss, which processes CSS using PostCSS. However, the plugin does not make use of Rollup’s emitFile() hook, which can cause duplication and double-loading of assets.


CSS is generally handled by plugins in Webpack, like mini-css-extract-plugin or extract-loader. Typically the process involves parsing the CSS and generating require() statements for each subresource, then parsing those requires and the JavaScript-wrapped CSS back out in an extraction step. This allows Webpack to handle subresources using the same mechanisms it uses for other file types.

This test passes in Webpack because the [contenthash] template field for filenames changes only when the output changes. When a hash is invalidated, the filename is updated and its importer is also updated to reflect this, cascading the hash change upwards.