Cache-bust local css/js so a deploy is picked up immediately

Apache serves this site's static files with only Last-Modified/ETag and no
Cache-Control or Expires, so browsers apply heuristic freshness and can hold
a stale stylesheet for hours. Deploys here are a file copy, so nothing else
signals a change. During development this masked CSS edits three separate
times; on the live site a returning customer would keep the old stylesheet
after a deploy with no way to know.

Adds asset_ver() (autoloaded), which returns the asset URL with the file's
mtime appended, so the URL itself changes whenever the file does. Falls back
to the plain URL when the file is missing, so a bad path degrades to the
previous behaviour rather than warning.

All 54 local css/js links now route through it, across the four *-head.php
and four *-skeleton-bottom.php includes.

Notes
- Remote assets are deliberately untouched: Google Fonts, momentjs,
  cookieyes and the googleapis jQuery keep their own URLs. Only files inside
  the webroot are versioned.
- vertical-head.php has one dynamic path (assets/css/<?= $vertical['css'] ?>)
  which is special-cased; a generic rewrite would swallow the nested <?php.
- Images are NOT versioned. Replacing an image under the same filename can
  still serve stale.
- head.php is shared by the public home page and the whole admin portal;
  /login verified to still render with versioned CSS.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Ubuntu
2026-08-15 09:00:40 +00:00
co-authored by Claude Opus 5
parent de38aae8d5
commit 7f51a795a5
10 changed files with 102 additions and 55 deletions
+47
View File
@@ -0,0 +1,47 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Cache-busting asset URLs.
*
* Apache serves this site's static files with only Last-Modified/ETag and no
* Cache-Control or Expires header, so browsers fall back to heuristic freshness
* and can hold a stale stylesheet for hours after a deploy. Because deploys here
* are a file copy, there is no other signal that anything changed - during
* development this repeatedly masked CSS edits, and on the live site a returning
* customer would keep the old stylesheet with no way to know.
*
* Appending the file's mtime makes the URL itself change whenever the file does,
* so the browser is forced to refetch. It costs one stat() per asset, which PHP
* caches per request.
*/
if ( ! function_exists('asset_ver'))
{
/**
* Absolute URL for a local asset, with ?v=<mtime> appended.
*
* Only for files inside the webroot. Remote assets (Google Fonts, the
* cookieyes script, momentjs) must keep their own URLs untouched.
*
* Falls back to the plain URL when the file is missing, so a bad path
* degrades to the previous behaviour rather than emitting a warning.
*
* @param string $path webroot-relative, e.g. 'assets/css/main.css'
* @return string
*/
function asset_ver($path)
{
$path = ltrim((string) $path, '/');
$url = rtrim(SITEURL, '/').'/'.$path;
$file = FCPATH.$path;
if ($path !== '' && is_file($file))
{
$url .= (strpos($url, '?') === FALSE ? '?' : '&').'v='.filemtime($file);
}
return $url;
}
}