-
Notifications
You must be signed in to change notification settings - Fork 258
Description
Problem
The current build process has an architectural flaw that causes CloudFront cache issues when HTML templates change but CSS source files don't.
Current Build Process
- Webpack generates
assets/css/bundle.css(4.2MB with all Tailwind utilities) - Hugo runs
{{ $css := resources.Get "css/bundle.css" | fingerprint }}:- Computes SHA-256 hash from the 4.2MB source file
- Copies to
public/css/bundle.<hash>.css - Embeds hash in HTML:
<link href="/css/bundle.<hash>.css">
- PurgeCSS runs
scripts/minify-css.js:- Scans
public/**/*.htmlto find used CSS classes - Removes unused classes
- Modifies the file in-place in
public/css/ - File content changes dramatically (4.2MB → 350KB), but filename stays the same
- Scans
The Issue
The filename hash is based on the source CSS (before PurgeCSS), but CloudFront serves the purged CSS (after PurgeCSS). This creates a mismatch:
- Scenario: A new HTML template is added (e.g., reinvent page with
-translate-y-1/2class) - Source CSS: Unchanged (Tailwind already has the utility with
purge: false) - Hugo fingerprint: Same hash
34a92c...(computed from unchanged source) - PurgeCSS output: Different content (now keeps
-translate-y-1/2because it sees it in HTML) - Result: Same filename
bundle.34a92c....css, but different content - CloudFront: Caches old version for 1 year, serves stale CSS
Real-World Impact
This happened with PR #16230 (reinvent 2025 page):
- Production CSS: 357,582 bytes, missing
-translate-y-1/2class - Local CSS: 358,401 bytes, contains the class
- Same filename:
bundle.34a92c610741a649ee861f5906ac268dfe493817c24e40a169872e8ff4437da4.css - CloudFront served 20+ hour old cached version
Workarounds Used
Immediate fix: Modified theme/tailwind.config.js to force a new fingerprint (this PR/commit)
Why it works: Changing Tailwind config → new source CSS → new fingerprint → new filename → CloudFront cache miss
Proper Long-Term Solutions
Option 1: Post-Build Fingerprinting (Recommended)
Compute fingerprint after PurgeCSS runs:
# build-site.sh
hugo build # Don't fingerprint CSS yet
yarn run minify-css # PurgeCSS runs
node scripts/fingerprint-and-update-html.js # Compute hash, rename CSS, update HTMLPros:
- Fingerprint perfectly matches served content
- No cache issues
- Clean architecture
Cons:
- Requires post-processing 4,918 HTML files to update CSS references
- Estimated work: 1-2 days
Option 2: CloudFront Cache Invalidation
Add invalidation to deployment:
# scripts/ci-push.sh
./scripts/run-pulumi.sh update
./scripts/invalidate-cdn-cache.sh # New scriptPros:
- Simple to implement (1-2 hours)
- No architectural changes
Cons:
- Doesn't fix root cause
- Invalidations take 1-5 minutes to propagate
- First 1000 invalidations/month are free, then $0.005 each
Option 3: Disable PurgeCSS
Ship the full 4.2MB CSS file:
Pros:
- Perfect fingerprinting
- Zero cache issues
Cons:
- 12x larger CSS download
- Not acceptable for production
Recommendation
Implement Option 2 (CloudFront invalidation) as a short-term fix, then Option 1 (post-build fingerprinting) as the proper long-term solution when time permits.
Related Files
layouts/partials/assets.html- Where Hugo fingerprinting happensscripts/build-site.sh- Build orchestrationscripts/minify-css.js- PurgeCSS implementationinfrastructure/index.ts- CloudFront configuration (lines 503-506, 509-512)
References
- Original issue: Reinvent page CSS not working in production
- Root cause investigation: CSS fingerprint doesn't match purged content
- Immediate fix: Force new fingerprint via Tailwind config change