How the OpenMage CSS/JS Minify Module Detects File Changes for Cache Invalidation

The OpenMage CSS/JS Minify module detects file changes by reading the modification timestamp (filemtime) of source assets and embedding that timestamp into the minified filename, automatically generating new cache-busted URLs whenever the original files are edited.

The fballiano/openmage-cssjs-minify extension implements a zero-configuration cache invalidation system that ensures browsers always receive the latest minified versions of JavaScript and CSS files. By combining filesystem metadata with deterministic hashing, the module eliminates the need for manual version increments or query-string cache busting while maintaining a clean storage directory through automated garbage collection.

Timestamp-Based Cache Busting Mechanism

The module relies on the modification time of source files as the sole trigger for cache invalidation. When the observer processes the HTML response, it resolves each asset's physical path and queries the filesystem for the last modification timestamp.

The filemtime Strategy

During the httpResponseSendBefore event, the observer intercepts the HTTP response and parses all <script src="…"> and <link href="…"> tags. For each resolved physical path, it calls filemtime($baseDir . $path) to capture the integer timestamp of the last file modification. This occurs at line 45 for JavaScript files and line 75 for CSS files within the observer class.

Hash Generation Logic

The module constructs a unique filename by concatenating an MD5 hash of the file's relative path with the modification timestamp:

$hash = md5($path) . "-$time.js";  // or "-$time.css"

This hash becomes the physical filename stored under media/fbminify. Because the timestamp changes whenever a developer saves the source file, the resulting hash changes, forcing the module to generate a new minified copy on the next request. The HTML output is then rewritten to reference this new filename, effectively invalidating both server-side and browser caches.

Implementation Details in Observer.php

The core logic resides in app/code/community/Fballiano/CssjsMinify/Model/Observer.php, which handles asset extraction, timestamp reading, and on-the-fly minification using the matthiasmullie/minify library.

JavaScript Processing

When processing JavaScript references, the observer extracts the URL components, verifies the file exists on disk, and generates the cache-busted filename:

$path = $urlComponents['path'];
if (file_exists($baseDir . $path)) {
    $time = filemtime($baseDir . $path);                     // ← line 45
    $hash = md5($path) . "-$time.js";                       // ← line 46
    if (!file_exists($minifiedDir . $hash)) {
        // minify or copy the original file into $minifiedDir/$hash
    }
    $matches[2] = $minifiedUrl . $hash;                     // replace URL in HTML
}

CSS Processing

The identical logic applies to stylesheet processing at line 75, ensuring consistent cache invalidation across both asset types:

$path = $urlComponents['path'];
if (file_exists($baseDir . $path)) {
    $time = filemtime($baseDir . $path);                     // ← line 75
    $hash = md5($path) . "-$time.css";                      // ← line 76
    // …create minified file if missing…
    $matches[2] = $minifiedUrl . $hash;                     // replace URL
}

Daily Cleanup of Legacy Files

The module implements a daily cron job (dailyCron) to prevent the media/fbminify directory from consuming excessive disk space. As implemented in lines 22–34 of the observer, this routine scans the minified folder and removes obsolete versions while preserving the most recent build of each asset.

The dailyCron Method

The cleanup algorithm sorts files in descending order and groups them by their MD5-derived hash. Since filenames follow the pattern {hash}-{timestamp}.{ext}, the script identifies duplicate hashes and deletes all but the newest file (which appears first due to the descending sort):

$files = @scandir($minifiedDir, SCANDIR_SORT_DESCENDING);
$lastHash = null;
foreach ($files as $file) {
    // strip extension and split "md5hash-timestamp.ext"
    $fileName = preg_replace('/\.(js|css)$/', '', $file);
    $hash = explode('-', $fileName)[0];

    // delete older files that share the same hash
    if ($hash == $lastHash) {
        @unlink("{$minifiedDir}/{$file}");
        continue;
    }
    $lastHash = $hash;
}

This garbage collection strategy ensures that only the latest minified assets remain on disk, while historical versions are purged automatically without manual intervention.

Summary

  • Modification time detection: The module uses filemtime() at lines 45 and 75 to detect when source files change.
  • Hash-based filenames: It combines MD5 hashes with timestamps to generate unique filenames like md5($path)-$time.js.
  • Storage location: Minified files are stored in media/fbminify and referenced directly in the rewritten HTML.
  • Automated cleanup: A daily cron job (lines 22–34) removes stale versions, keeping only the latest file per source asset.
  • Zero configuration: The system requires no manual version management or query parameters to bust caches.

Frequently Asked Questions

What triggers cache invalidation in the OpenMage CSS/JS Minify module?

Cache invalidation triggers automatically when the filesystem modification timestamp (filemtime) of a source JavaScript or CSS file changes. The module detects this during page rendering and generates a new minified filename incorporating the updated timestamp, causing browsers to fetch the fresh asset.

How does the module prevent serving outdated minified files?

The module prevents serving stale content by rewriting HTML references to point exclusively to the latest hash-based filename. Since the filename itself changes when the source file is edited, browsers and CDNs treat it as a new resource. The daily cleanup routine then removes older versions from the media/fbminify directory to conserve disk space.

Where are the minified files stored?

Minified files are stored in the media/fbminify directory within the Magento root. The module organizes files using the pattern {md5_hash}-{timestamp}.{extension}, allowing the daily cron job to easily identify and group versions of the same source file for cleanup purposes.

Does this approach work with CDN or browser caching?

Yes, the timestamp-in-filename approach provides aggressive cache-busting compatible with CDNs and browser caching. Because the URL changes whenever the file content changes, external caches automatically fetch the new version on the next request without requiring manual cache purging or version query strings.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →