Add filemtime-based cache busting for styles.css and main.js

Safari caches static assets far more aggressively than Chrome/Firefox
and largely ignores heuristic revalidation, so a new deploy could keep
showing the old CSS/JS indefinitely even on a hard refresh — no
Cache-Control headers were set anywhere to guide it otherwise.

inc/assets.php adds asset_version($path), which appends a ?v=<mtime>
query string derived from the file's last-modified time. Every page
that links styles.css or main.js (index.php, menu.php, and all five
admin pages) now uses it, so the URL changes whenever the file's
contents change and browsers always fetch the new version.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
David DyerandClaude Sonnet 5 committed 2026-09-02 10:23:40 -04:00
1 parent 375407245c
commit bbe84e8614
8 files changed
+41 -9

No files matched your search

+2 -1
View File
@@ -2,6 +2,7 @@
declare(strict_types=1);
require_once __DIR__ . '/../inc/auth.php';
require_once __DIR__ . '/../inc/assets.php';
require_once __DIR__ . '/../inc/hours.php';
require_auth();
@@ -72,7 +73,7 @@ $justSaved = !$_POST && isset($_GET['saved']);
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,400;0,9..144,600;1,9..144,400&family=Karla:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500&display=swap" rel="stylesheet">
<link rel="stylesheet" href="../styles.css">
<link rel="stylesheet" href="../styles.css<?= asset_version('styles.css') ?>">
</head>
<body>
+2 -1
View File
@@ -1,5 +1,6 @@
<?php
require_once __DIR__ . '/../inc/auth.php';
require_once __DIR__ . '/../inc/assets.php';
require_once __DIR__ . '/../inc/announcements.php';
require_auth();
@@ -108,7 +109,7 @@ $announcements = get_all_announcements();
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,400;0,9..144,600;1,9..144,400&family=Karla:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500&display=swap" rel="stylesheet">
<link rel="stylesheet" href="../styles.css">
<link rel="stylesheet" href="../styles.css<?= asset_version('styles.css') ?>">
</head>
<body>
+2 -1
View File
@@ -1,5 +1,6 @@
<?php
require_once __DIR__ . '/../inc/auth.php';
require_once __DIR__ . '/../inc/assets.php';
$error = '';
@@ -24,7 +25,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Fraunces:wght@600&family=Karla:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500&display=swap" rel="stylesheet">
<link rel="stylesheet" href="../styles.css">
<link rel="stylesheet" href="../styles.css<?= asset_version('styles.css') ?>">
</head>
<body class="admin-login-body">
<div class="login-card">
+2 -1
View File
@@ -2,6 +2,7 @@
declare(strict_types=1);
require_once __DIR__ . '/../inc/auth.php';
require_once __DIR__ . '/../inc/assets.php';
require_once __DIR__ . '/../inc/menu.php';
require_auth();
@@ -186,7 +187,7 @@ $menuEnabled = is_menu_enabled();
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,400;0,9..144,600;1,9..144,400&family=Karla:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500&display=swap" rel="stylesheet">
<link rel="stylesheet" href="../styles.css">
<link rel="stylesheet" href="../styles.css<?= asset_version('styles.css') ?>">
</head>
<body data-csrf-token="<?= htmlspecialchars(get_csrf_token(), ENT_QUOTES, 'UTF-8') ?>">
+2 -1
View File
@@ -2,6 +2,7 @@
declare(strict_types=1);
require_once __DIR__ . '/../inc/auth.php';
require_once __DIR__ . '/../inc/assets.php';
require_once __DIR__ . '/../inc/users.php';
require_auth();
@@ -173,7 +174,7 @@ $users = get_all_users();
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,400;0,9..144,600;1,9..144,400&family=Karla:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500&display=swap" rel="stylesheet">
<link rel="stylesheet" href="../styles.css">
<link rel="stylesheet" href="../styles.css<?= asset_version('styles.css') ?>">
</head>
<body data-csrf-token="<?= htmlspecialchars(get_csrf_token(), ENT_QUOTES, 'UTF-8') ?>" data-current-user-id="<?= (int) $user['id'] ?>">
+25
View File
@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
/**
* Cache-busting query string for a static asset (styles.css, main.js),
* derived from the file's last-modified time.
*
* Without this, browsers — Safari especially, which caches static
* assets far more aggressively than Chrome/Firefox and largely ignores
* heuristic revalidation — can keep serving a stale styles.css/main.js
* indefinitely after a deploy, even on a hard refresh. Appending
* ?v=<mtime> changes the URL every time the file's contents change, so
* it's always treated as a brand new resource rather than a cached one.
*
* Usage: <link rel="stylesheet" href="styles.css<?= asset_version('styles.css') ?>">
* <link rel="stylesheet" href="../styles.css<?= asset_version('styles.css') ?>">
*/
function asset_version(string $relativePath): string
{
$path = dirname(__DIR__) . '/' . ltrim($relativePath, '/');
$mtime = @filemtime($path);
return $mtime !== false ? '?v=' . $mtime : '';
}
+3 -2
View File
@@ -1,5 +1,6 @@
<?php
require __DIR__ . '/config.php';
require __DIR__ . '/inc/assets.php';
require __DIR__ . '/inc/google-reviews.php';
require __DIR__ . '/inc/announcements.php';
require __DIR__ . '/inc/hours.php';
@@ -17,7 +18,7 @@ $siteUrl = rtrim(SITE_URL, '/');
<title>The Old Fitz Public House</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="styles.css">
<link rel="stylesheet" href="styles.css<?= asset_version('styles.css') ?>">
<link href="https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,300;0,9..144,400;0,9..144,500;0,9..144,600;0,9..144,700;1,9..144,400;1,9..144,500&family=Karla:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500&display=swap" rel="stylesheet">
<meta name="description" content="The Old Fitz Public House — a traditional Irish pub in Marion, Indiana. Open Thursday through Saturday, 4–10pm. Family friendly, no reservations.">
@@ -284,7 +285,7 @@ $siteUrl = rtrim(SITE_URL, '/');
</div>
</footer>
<script src="main.js" defer></script>
<script src="main.js<?= asset_version('main.js') ?>" defer></script>
</body>
</html>
+3 -2
View File
@@ -2,6 +2,7 @@
declare(strict_types=1);
require __DIR__ . '/config.php';
require __DIR__ . '/inc/assets.php';
require __DIR__ . '/inc/announcements.php';
require __DIR__ . '/inc/menu.php';
require __DIR__ . '/inc/hours.php';
@@ -38,7 +39,7 @@ $siteUrl = rtrim(SITE_URL, '/');
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="styles.css">
<link rel="stylesheet" href="styles.css<?= asset_version('styles.css') ?>">
<link href="https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,300;0,9..144,400;0,9..144,500;0,9..144,600;0,9..144,700;1,9..144,400;1,9..144,500&family=Karla:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500&display=swap" rel="stylesheet">
</head>
@@ -210,7 +211,7 @@ $siteUrl = rtrim(SITE_URL, '/');
</div>
</footer>
<script src="main.js" defer></script>
<script src="main.js<?= asset_version('main.js') ?>" defer></script>
</body>