Persistent CSS 404 errors on Cloudflare Workers deployment despite file existing and configuration appearing correct.
-
Wrangler Asset Manifest Corruption
- Wrangler caches asset manifests between deployments
- When a deployment fails (e.g., broken Worker code), manifest can become corrupted
- Subsequent deployments with "unchanged" assets skip re-upload, leaving stale/broken manifest
- Result: Worker can't find assets that were supposedly uploaded
-
Incremental Upload System
# What we saw in logs: "No updated asset files to upload. Proceeding with deployment..." # Translation: Wrangler detected no changes, skipped upload, used old manifest
-
Configuration Confusion
[site]config causes HTTP 500 errors (doesn't work with current Wrangler)[assets]config with__STATIC_CONTENT__binding is correct- But even correct config can't fix corrupted asset manifest
- ❌ Tweaking CSS paths and
extra_csssettings repeatedly - ❌ Switching between
[site]and[assets]configs (made it worse) - ❌ Force re-uploading by modifying CSS file (Wrangler still skipped upload)
- ❌ Multiple commits trying path variations
Eliminated the problem entirely by inlining CSS in overrides/main.html:
{% block styles %}
{{ super() }}
<style>
/* All custom CSS here */
</style>
{% endblock %}Why this works:
- No external files to serve = no 404 errors
- No dependency on Wrangler asset system
- CSS guaranteed to load with page
- Simpler deployment (one less moving part)
Working config:
# wrangler.toml
[assets]
directory = "./site"
binding = "__STATIC_CONTENT__"// index.js
export default {
async fetch(request, env, ctx) {
return await env.__STATIC_CONTENT__.fetch(request);
}
};Broken config (DO NOT USE):
[site]
bucket = "./site" # Causes HTTP 500 errorsSymptoms:
- Assets exist locally in
site/directory - GitHub Actions shows successful upload
- Production returns 404 for specific assets
- Other assets work fine
Diagnosis:
# Check deployment logs for:
"No updated asset files to upload" # Bad: skipped upload
"Uploaded X new assets" # Good: actually uploadedFix options:
- Nuclear option: Delete and recreate Cloudflare Worker
- Inline assets: Embed CSS/JS directly in HTML
- Force re-upload: Not reliable (Wrangler uses content hashing)
Inline if:
- File is small (<5KB)
- Deployment system is unreliable
- External file keeps causing 404s
- Asset is critical for page functionality
Keep external if:
- File is large (images, videos, large JS bundles)
- Needs separate caching strategy
- Shared across many pages
Check in this order:
- Does file exist in built
site/directory? - Does HTML reference correct path?
- Do OTHER assets from same directory work?
- If yes: asset manifest issue
- If no: path/config issue
- Check deployment logs for actual upload
- Try Workers URL directly (bypass CDN cache)
- Custom styles are inlined in
overrides/main.html(noextra_cssdependency) - Ensure
mkdocs.yamlpoints tooverridesviacustom_dir - Use absolute paths in markdown for assets that must work on Workers
- Custom domain CDN caching can hide deployment issues
Trade-offs accepted:
- ❌ CSS can't be cached separately (~3KB added to each page)
- ✅ Zero external file dependencies
- ✅ Guaranteed to work
- ✅ Simpler deployment
- ✅ No more asset manifest issues
Final verdict: For this project (small custom CSS, persistent deployment issues), inlining was the right choice.
-
Monitor deployment logs carefully:
gh run view <run-id> --log | grep -E "(Uploaded|Skipped)"
-
Verify pages after deployment:
curl -I https://your-site.com/ curl -I https://your-worker.workers.dev/
-
Keep custom assets minimal:
- Small CSS/JS → inline it
- Large files → use CDN or object storage
-
Document working configuration:
- Keep
wrangler.tomlandindex.jsin version control - Add comments explaining why config is structured that way
- Note any known issues with alternatives
- Keep
When you see commit message like:
- "Fix CSS serving: Use [site] instead of [assets]"
Don't assume it's still correct! Check:
- Were there subsequent reverts?
- Did later commits change it back?
- What's the actual working config NOW?
In our case, commit 7c76140 claimed to "fix" by using [site], but was immediately reverted because it broke the entire site.
Original problem: CSS 404 errors Thought it was: Path configuration issue Actually was: Wrangler asset manifest corruption + incremental upload system Tried fixing: Multiple config changes, path variations, forced re-uploads What worked: Inlining CSS to eliminate external file dependency entirely
Time spent debugging: ~2 hours Lines of CSS: 133 Deployments attempted: 8 Lesson: Sometimes the simplest solution (inline it) beats debugging complex systems.