Browsers normally cache resource files like JavaScript, CSS and images so that next time you go back to the same site, it doesn't have to download them again. This mechanism is great for saving time and bandwidth, but sometimes we can run into problems because of it. To quote MDN:
In modern web development, JavaScript and CSS resources are frequently updated as development progresses. Also, if the versions of JavaScript and CSS resources that a client uses are out of sync, the display will break.
Browsers will assume that if they're asked to load https://example.com/bundle.js
and they've loaded it before, a cached version can probably be used. Therefore, if we want to ensure that browsers are loading the right version of a file, we should modify the URL when the file is updated.
MDN suggests the following options:
# version in filename
bundle.v123.js
# version in query
bundle.js?v=123
# hash in filename
bundle.YsAIAAAA-QG4G6kCMAMBAAAAAAAoK.js
# hash in query
bundle.js?v=YsAIAAAA-QG4G6kCMAMBAAAAAAAoK
I find option 1 and 2 a bit tedious because they will usually involve me having to manually change a version string whenever I've updated something.
Instead of a hash, I tend to use the filemtime function in PHP to get the timestamp for when the file was last modified.
$mtime = filemtime( '/var/www/example-site/js/bundle.js' );
echo '<script src="https://example-site.com/js/bundle.js?ver=' . $mtime . '"></script>';
Obviously the same approach works for both JS and CSS. Here's an example of loading CSS in WordPress:
$mtime = filemtime( get_template_directory() . '/bundle.css' );
wp_enqueue_style( 'bundle', get_template_directory_uri() . '/bundle.css', array(), $mtime );
More info on MDN:
I may be misremembering, but didn't it used to be the case that most browsers would not cache a CSS or JavaScript file if it had query parameters, regardless of whether those changed with new versions? For example, https://www.example.com/js/core.js?v=1 would not be cached, no matter what value v was set to in the URL. There seems to be some disagreement on the web. It looks like in the past some browsers and caching proxies would never cache resources that had a query string in the URL, but that's now very rare.