
Configure Cache Busting
- 55 installs
- 49 repo stars
- Updated August 4, 2026
- laurigates/claude-plugins
Helps with ai & agent building tasks.
About
configure-cache-busting is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- configure-cache-busting
- AI & Agent Building
- AI-coding skill
Configure Cache Busting by the numbers
- 55 all-time installs (skills.sh)
- Ranked #6,762 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/laurigates/claude-plugins --skill configure-cache-bustingAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 55 |
|---|---|
| repo stars | ★ 49 |
| Last updated | August 4, 2026 |
| Repository | laurigates/claude-plugins ↗ |
What it does
Helps with ai & agent building tasks.
Files
When to Use This Skill
| Use this skill when... | Use another approach when... |
|---|---|
| Configuring content hashing for Next.js or Vite builds | Optimizing server-side caching (nginx, CDN config directly) |
| Setting up CDN cache headers for Vercel or Cloudflare | Debugging build output issues (system-debugging agent) |
| Verifying cache-busting compliance after a framework upgrade | Configuring general CI/CD workflows (/configure:workflows) |
| Adding build verification scripts for hashed assets | Setting up container builds (/configure:container) |
| Auditing static asset caching strategy across a project | Profiling frontend performance (browser devtools) |
Context
- Project root: !
pwd - Package files: !
find . -maxdepth 1 -name 'package.json' - Next.js config: !
find . -maxdepth 1 -name 'next.config.*' - Vite config: !
find . -maxdepth 1 -name 'vite.config.*' - Build output: !
find . -maxdepth 1 -type d \( -name '.next' -o -name 'dist' -o -name 'out' \) - CDN config: !
find . -maxdepth 2 \( -path './vercel.json' -o -path './_headers' -o -path './_redirects' -o -path './public/_headers' \) - Project standards: !
find . -maxdepth 1 -name '.project-standards.yaml'
Parameters
Parse from command arguments:
--check-only: Report compliance status without modifications (CI/CD mode)--fix: Apply fixes automatically without prompting--framework <nextjs|vite>: Override framework detection--cdn <cloudflare|vercel|none>: Specify CDN provider for cache header configuration
Execution
Execute this cache-busting configuration check:
Step 1: Detect project framework
Identify the framework from file structure:
| Indicator | Framework | Config File |
|---|---|---|
next.config.js or next.config.mjs | Next.js | next.config.* |
next.config.ts | Next.js | next.config.ts |
vite.config.js or vite.config.ts | Vite | vite.config.* |
.next/ directory | Next.js (built) | Detection only |
dist/ directory + vite in package.json | Vite (built) | Detection only |
Check package.json dependencies for "next" or "vite".
If both detected, prompt user to specify with --framework. If neither detected, report unsupported and exit.
Step 2: Analyze current cache-busting state
For the detected framework, read config files and check:
Next.js - Read next.config.js/ts and check:
- [ ]
generateBuildIdconfigured for deterministic builds - [ ]
assetPrefixconfigured for CDN - [ ]
compress: trueenabled - [ ]
poweredByHeader: falsefor security - [ ]
generateEtagsconfigured - [ ] Cache headers configured in
headers()function
Vite - Read vite.config.js/ts and check:
- [ ]
build.rollupOptions.output.entryFileNamesuses[hash] - [ ]
build.rollupOptions.output.chunkFileNamesuses[hash] - [ ]
build.rollupOptions.output.assetFileNamesuses[hash] - [ ]
build.manifest: truefor SSR/manifest-based routing - [ ]
build.cssCodeSplitconfigured appropriately
Step 3: Detect CDN provider
Identify CDN from project files:
| Indicator | CDN Provider |
|---|---|
vercel.json exists | Vercel |
.vercelignore exists | Vercel |
_headers in root or public/ | Cloudflare Pages |
_redirects exists | Cloudflare Pages / Netlify |
wrangler.toml exists | Cloudflare Workers/Pages |
| None of the above | Generic / None |
Step 4: Generate compliance report
Print a formatted compliance report:
Cache-Busting Compliance Report
================================
Project: [name]
Framework: [Next.js 14.x | Vite 5.x]
CDN Provider: [Vercel | Cloudflare | None detected]
Framework Configuration:
Config file next.config.js [EXISTS | MISSING]
Asset hashing [hash] in filenames [ENABLED | DISABLED]
Build manifest manifest files [GENERATED | MISSING]
Deterministic builds Build ID configured [PASS | NOT SET]
Compression gzip/brotli enabled [PASS | DISABLED]
Cache Headers:
Static assets immutable, 1y [CONFIGURED | MISSING]
HTML files no-cache, must-revalidate [CONFIGURED | MISSING]
API routes varies by route [CONFIGURED | N/A]
CDN configuration vercel.json/_headers [EXISTS | MISSING]
Build Output (if built):
Hashed filenames app.[hash].js [DETECTED | NOT BUILT]
Content addressing Unique hashes per version [PASS | DUPLICATE]
Manifest integrity Valid manifest.json [PASS | INVALID]
Overall: [X issues found]
Recommendations:
[List specific fixes needed]If --check-only, stop here.
Step 5: Apply configuration (if --fix or user confirms)
Based on detected framework, create or update config files using templates from REFERENCE.md:
1. Next.js: Update next.config.js/ts with deterministic builds, compression, cache headers 2. Vite: Update vite.config.js/ts with content hashing, manifest, chunk splitting
Step 6: Configure CDN cache headers
Based on detected CDN provider, create or update cache header configuration using templates from REFERENCE.md:
- Vercel: Create/update
vercel.jsonwith header rules - Cloudflare Pages: Create
public/_headerswith cache rules - Generic: Provide nginx configuration reference
Step 7: Add build verification
Create scripts/verify-cache-busting.js to verify content hashing works after build. Add package.json scripts for build verification. Use the verification script template from REFERENCE.md.
Step 8: Configure CI/CD verification
Add cache-busting verification step to GitHub Actions workflow. Use the CI workflow template from REFERENCE.md.
Step 9: Update standards tracking
Update .project-standards.yaml:
standards_version: "2025.1"
last_configured: "[timestamp]"
components:
cache-busting: "2025.1"
cache-busting-framework: "[nextjs|vite]"
cache-busting-cdn: "[vercel|cloudflare|none]"
cache-busting-verified: trueStep 10: Print final report
Print a summary of changes applied, cache strategy overview, and next steps for verification.
For detailed configuration templates and code examples, see REFERENCE.md.
Agentic Optimizations
| Context | Command |
|---|---|
| Quick compliance check | /configure:cache-busting --check-only |
| Auto-fix all issues | /configure:cache-busting --fix |
| Next.js project only | /configure:cache-busting --fix --framework nextjs |
| Vite project only | /configure:cache-busting --fix --framework vite |
| Cloudflare CDN headers | /configure:cache-busting --fix --cdn cloudflare |
| Vercel CDN headers | /configure:cache-busting --fix --cdn vercel |
Output
Provide: 1. Compliance report with framework and CDN configuration status 2. List of changes made (if --fix) or proposed (if interactive) 3. Verification instructions and commands 4. CDN cache header examples 5. Next steps for deployment and monitoring
See Also
/configure:all- Run all compliance checks/configure:status- Quick compliance overview/configure:workflows- GitHub Actions workflow standards/configure:dockerfile- Container configuration with build caching- Next.js Documentation - https://nextjs.org/docs/pages/api-reference/next-config-js
- Vite Documentation - https://vitejs.dev/config/build-options.html
- Web.dev Caching Guide - https://web.dev/http-cache/
Cache-Busting Reference
Configuration templates and code examples for cache-busting strategies.
Next.js Configuration
next.config.js Template
/** @type {import('next').NextConfig} */
const nextConfig = {
// Deterministic build IDs for reproducible builds
generateBuildId: async () => {
return process.env.GIT_COMMIT_SHA || process.env.VERCEL_GIT_COMMIT_SHA || 'development';
},
// CDN asset prefix (optional)
// assetPrefix: process.env.CDN_URL || '',
// Enable compression
compress: true,
// Remove X-Powered-By header for security
poweredByHeader: false,
// Configure ETags for caching
generateEtags: true,
// Image optimization
images: {
domains: ['your-cdn-domain.com'],
formats: ['image/avif', 'image/webp'],
},
// Headers for cache control
async headers() {
return [
{
source: '/_next/static/:path*',
headers: [
{ key: 'Cache-Control', value: 'public, max-age=31536000, immutable' },
],
},
{
source: '/_next/image/:path*',
headers: [
{ key: 'Cache-Control', value: 'public, max-age=86400, s-maxage=31536000, stale-while-revalidate' },
],
},
{
source: '/:path*.html',
headers: [
{ key: 'Cache-Control', value: 'public, max-age=0, must-revalidate' },
],
},
{
source: '/api/:path*',
headers: [
{ key: 'Cache-Control', value: 'no-store, no-cache, must-revalidate' },
],
},
];
},
webpack: (config, { isServer }) => {
config.optimization = {
...config.optimization,
moduleIds: 'deterministic',
};
return config;
},
};
module.exports = nextConfig;next.config.ts Template (TypeScript)
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
generateBuildId: async () => {
return process.env.GIT_COMMIT_SHA || process.env.VERCEL_GIT_COMMIT_SHA || 'development';
},
compress: true,
poweredByHeader: false,
generateEtags: true,
async headers() {
return [
{
source: '/_next/static/:path*',
headers: [
{ key: 'Cache-Control', value: 'public, max-age=31536000, immutable' },
],
},
{
source: '/:path*.html',
headers: [
{ key: 'Cache-Control', value: 'public, max-age=0, must-revalidate' },
],
},
];
},
};
export default nextConfig;Vite Configuration
vite.config.js Template
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue'; // or @vitejs/plugin-react
export default defineConfig({
plugins: [vue()],
build: {
manifest: true,
cssCodeSplit: true,
sourcemap: false, // Set to 'hidden' for sentry integration
rollupOptions: {
output: {
entryFileNames: 'assets/[name].[hash].js',
chunkFileNames: 'assets/[name].[hash].js',
assetFileNames: (assetInfo) => {
const info = assetInfo.name.split('.');
const ext = info[info.length - 1];
if (/\.(png|jpe?g|gif|svg|webp|avif)$/i.test(assetInfo.name)) {
return 'assets/images/[name].[hash].[ext]';
}
if (/\.(woff2?|eot|ttf|otf)$/i.test(assetInfo.name)) {
return 'assets/fonts/[name].[hash].[ext]';
}
if (/\.css$/i.test(assetInfo.name)) {
return 'assets/css/[name].[hash].[ext]';
}
return 'assets/[name].[hash].[ext]';
},
manualChunks: (id) => {
if (id.includes('node_modules')) {
if (id.includes('vue') || id.includes('react')) {
return 'vendor-framework';
}
if (id.includes('lodash') || id.includes('moment')) {
return 'vendor-utils';
}
return 'vendor';
}
},
},
},
assetsInlineLimit: 4096, // 4KB
chunkSizeWarningLimit: 500, // KB
},
preview: {
headers: {
'Cache-Control': 'public, max-age=600',
},
},
});vite.config.ts Template (TypeScript)
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
plugins: [vue()],
build: {
manifest: true,
cssCodeSplit: true,
sourcemap: false,
rollupOptions: {
output: {
entryFileNames: 'assets/[name].[hash].js',
chunkFileNames: 'assets/[name].[hash].js',
assetFileNames: (assetInfo): string => {
if (!assetInfo.name) return 'assets/[name].[hash][extname]';
const ext = assetInfo.name.split('.').pop();
if (/png|jpe?g|gif|svg|webp|avif/i.test(ext)) {
return 'assets/images/[name].[hash][extname]';
}
if (/woff2?|eot|ttf|otf/i.test(ext)) {
return 'assets/fonts/[name].[hash][extname]';
}
if (ext === 'css') {
return 'assets/css/[name].[hash][extname]';
}
return 'assets/[name].[hash][extname]';
},
manualChunks: (id) => {
if (id.includes('node_modules')) {
if (id.includes('vue') || id.includes('react')) {
return 'vendor-framework';
}
return 'vendor';
}
},
},
},
},
});CDN Cache Headers
Vercel (vercel.json)
{
"headers": [
{
"source": "/_next/static/(.*)",
"headers": [
{ "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }
]
},
{
"source": "/static/(.*)",
"headers": [
{ "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }
]
},
{
"source": "/assets/(.*)",
"headers": [
{ "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }
]
},
{
"source": "/(.*).html",
"headers": [
{ "key": "Cache-Control", "value": "public, max-age=0, must-revalidate" }
]
},
{
"source": "/(.*)",
"headers": [
{ "key": "X-Content-Type-Options", "value": "nosniff" },
{ "key": "X-Frame-Options", "value": "DENY" },
{ "key": "X-XSS-Protection", "value": "1; mode=block" }
]
}
]
}Cloudflare Pages (public/_headers)
# Static assets with content hashes - aggressive caching
/_next/static/*
Cache-Control: public, max-age=31536000, immutable
/static/*
Cache-Control: public, max-age=31536000, immutable
/assets/*
Cache-Control: public, max-age=31536000, immutable
# HTML files - always revalidate
/*.html
Cache-Control: public, max-age=0, must-revalidate
/
Cache-Control: public, max-age=0, must-revalidate
# Security headers for all routes
/*
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Referrer-Policy: strict-origin-when-cross-originGeneric Nginx Configuration
server {
listen 80;
server_name example.com;
root /usr/share/nginx/html;
index index.html;
location ~* ^/(assets|_next/static)/.*\.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
expires 1y;
add_header Cache-Control "public, max-age=31536000, immutable";
access_log off;
}
location ~* \.html?$ {
expires -1;
add_header Cache-Control "public, max-age=0, must-revalidate";
}
location / {
try_files $uri $uri/ /index.html;
add_header Cache-Control "public, max-age=0, must-revalidate";
}
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header X-XSS-Protection "1; mode=block" always;
}Service Worker Cache Strategy
public/sw.js Template
const CACHE_VERSION = 'v1';
const STATIC_CACHE = `static-${CACHE_VERSION}`;
const DYNAMIC_CACHE = `dynamic-${CACHE_VERSION}`;
const PRECACHE_URLS = [
'/',
'/index.html',
'/offline.html',
];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(STATIC_CACHE).then((cache) => {
return cache.addAll(PRECACHE_URLS);
})
);
self.skipWaiting();
});
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames
.filter((name) => name !== STATIC_CACHE && name !== DYNAMIC_CACHE)
.map((name) => caches.delete(name))
);
})
);
self.clients.claim();
});
self.addEventListener('fetch', (event) => {
const { request } = event;
const url = new URL(request.url);
// Cache-first for static assets with content hashes
if (url.pathname.match(/\.(js|css|png|jpg|jpeg|gif|svg|woff2?)$/)) {
event.respondWith(
caches.match(request).then((cached) => {
return cached || fetch(request).then((response) => {
return caches.open(DYNAMIC_CACHE).then((cache) => {
cache.put(request, response.clone());
return response;
});
});
})
);
return;
}
// Network-first for HTML
if (url.pathname.endsWith('.html') || url.pathname === '/') {
event.respondWith(
fetch(request)
.then((response) => {
return caches.open(DYNAMIC_CACHE).then((cache) => {
cache.put(request, response.clone());
return response;
});
})
.catch(() => {
return caches.match(request).then((cached) => {
return cached || caches.match('/offline.html');
});
})
);
return;
}
event.respondWith(fetch(request));
});Service Worker Registration
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker
.register('/sw.js')
.then((registration) => {
console.log('Service Worker registered:', registration.scope);
})
.catch((error) => {
console.log('Service Worker registration failed:', error);
});
});
}Build Verification Script
scripts/verify-cache-busting.js
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
function verifyNextBuild() {
const buildDir = path.join(process.cwd(), '.next/static');
if (!fs.existsSync(buildDir)) {
console.error('Build directory not found. Run `npm run build` first.');
process.exit(1);
}
const files = getAllFiles(buildDir);
const hashedFiles = files.filter(f => /\.[a-f0-9]{8,}\.(js|css)$/.test(f));
console.log(`Found ${hashedFiles.length} hashed files in ${files.length} total files`);
if (hashedFiles.length === 0) {
console.error('No content-hashed files found! Cache busting may not be working.');
process.exit(1);
}
const hashes = hashedFiles.map(f => f.match(/\.([a-f0-9]{8,})\./)?.[1]);
const uniqueHashes = new Set(hashes);
if (uniqueHashes.size < hashes.length) {
console.warn('Duplicate content hashes detected. This may indicate an issue.');
}
console.log('Cache busting verification passed!');
}
function verifyViteBuild() {
const distDir = path.join(process.cwd(), 'dist/assets');
if (!fs.existsSync(distDir)) {
console.error('Build directory not found. Run `npm run build` first.');
process.exit(1);
}
const files = getAllFiles(distDir);
const hashedFiles = files.filter(f => /\.[a-f0-9]{8,}\.(js|css)$/.test(f));
console.log(`Found ${hashedFiles.length} hashed files in ${files.length} total files`);
if (hashedFiles.length === 0) {
console.error('No content-hashed files found! Cache busting may not be working.');
process.exit(1);
}
console.log('Cache busting verification passed!');
}
function getAllFiles(dir, fileList = []) {
const files = fs.readdirSync(dir);
files.forEach(file => {
const filePath = path.join(dir, file);
if (fs.statSync(filePath).isDirectory()) {
getAllFiles(filePath, fileList);
} else {
fileList.push(filePath);
}
});
return fileList;
}
if (fs.existsSync('.next')) {
console.log('Verifying Next.js build...');
verifyNextBuild();
} else if (fs.existsSync('dist')) {
console.log('Verifying Vite build...');
verifyViteBuild();
} else {
console.error('No build output found. Run `npm run build` first.');
process.exit(1);
}CI/CD Workflow Template
name: CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm'
- run: npm ci
- name: Build
run: npm run build
env:
GIT_COMMIT_SHA: ${{ github.sha }}
- name: Verify cache busting
run: npm run cache:check
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: build-output
path: |
.next/
dist/Package.json Scripts
{
"scripts": {
"build": "next build",
"build:verify": "next build && node scripts/verify-cache-busting.js",
"cache:check": "node scripts/verify-cache-busting.js"
}
}Cache Strategy Summary
| Asset Type | Cache-Control | Duration |
|---|---|---|
| Hashed assets (JS, CSS) | public, max-age=31536000, immutable | 1 year |
| HTML files | public, max-age=0, must-revalidate | Always revalidate |
| Images | public, max-age=86400 | 1 day |
| API responses | no-store, no-cache | Never cached |