This content originally appeared on DEV Community and was authored by Tahsin Abrar
If you change a CSS file but users (often on mobile) still see the old styles, it’s almost always a caching problem. This post shows simple, practical fixes for Laravel/Blade projects from quick hacks to proper production setups with real examples you can copy.
The problem short story
Last month I deployed a tiny CSS change: a button color and spacing. On my desktop it looked fine. But several users (mostly on mobile) complained the site still showed the old look. I ran php artisan optimize:clear
, redeployed, even asked one user to refresh nothing. The cause? Browser (or CDN) still serving the old static file from cache.
Browsers, CDNs, and sometimes reverse proxies hold static files (CSS/JS) for speed. If the filename or URL does not change, they assume it’s the same file and keep using the cached copy.
Why php artisan optimize:clear
didn’t fix it
php artisan optimize:clear
clears Laravel’s server-side caches (views, routes, configs). It does not change the static asset file name or force browsers/CDNs to fetch a new style.css
. So browser cache still wins.
Practical fixes (from quick to best practice)
1) Best: Use versioned assets (Vite or Mix)
If you use Vite (Laravel 9+ default) or Laravel Mix, they produce versioned/hashed builds so each build has new filenames.
Vite (Blade):
@vite(['resources/css/app.css', 'resources/js/app.js'])
Build for production:
npm run build
Laravel Mix (webpack.mix.js):
mix
.js('resources/js/app.js', 'public/js')
.sass('resources/sass/app.scss', 'public/css')
.version(); // <-- generate hashed filenames
Then in Blade:
<link rel="stylesheet" href="{{ mix('css/app.css') }}">
Build for production:
npm run prod
Why this works: the filenames include a hash (e.g. app.abc123.css
) so when content changes, filename changes, forcing browsers & CDNs to fetch the new file.
2) Good quick fix: Use file modification time (filemtime
) better than time()
If you don’t use Mix/Vite yet, avoid ?v={{ time() }}
. time()
changes every request and breaks caching (bad for performance). Use the file’s last-modified timestamp so version only changes when file changes:
<link rel="stylesheet" href="{{ asset('css/style.css') }}?v={{ filemtime(public_path('css/style.css')) }}">
This way the query string changes only when the file actually changes.
3) Quick (but temporary) hack: ?v={{ time() }}
This forces a fresh download every time. It fixes the “old CSS” problem immediately but kills browser caching and hurts load time. Use only for debugging or one-time hotfix.
<link rel="stylesheet" href="{{ asset('css/style.css') }}?v={{ time() }}">
4) CDN / Cloudflare purge cache or configure properly
If you use Cloudflare or any CDN, it may serve cached CSS even after you update files.
- For a quick fix: Purge Cache → Purge Everything (Cloudflare) or purge specific URLs.
- Better: configure your CDN to respect file-hash/versioning. If you use hashed filenames, leave CDN cache long (1 year) safe because filename changes with content.
5) Set proper headers (server-side)
For static assets, set far-future Cache-Control
and use hashed filenames. Example nginx snippet:
location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg)$ {
expires 365d;
add_header Cache-Control "public, max-age=31536000, immutable";
}
immutable
means the browser can trust the cached file until the URL changes.
6) Laravel cache commands (still useful)
These clear Laravel caches (won’t force browser to fetch new CSS but good for general deploy):
php artisan view:clear
php artisan route:clear
php artisan config:clear
php artisan cache:clear
php artisan optimize:clear
How I fixed my real issue (short walkthrough)
- I had linked
public/css/style.css
directly. - I built assets with Mix and enabled
mix.version()
inwebpack.mix.js
. - Replaced
<link href="{{ asset('css/style.css') }}">
with<link href="{{ mix('css/style.css') }}">
in Blade. - Ran
npm run prod
, deployed newpublic/mix-manifest.json
and hashed files. - Purged Cloudflare cache for the CSS URL.
- Tested on mobile (incognito) new styles were visible immediately.
That flow solved the problem forever no more users complaining.
This content originally appeared on DEV Community and was authored by Tahsin Abrar