2.8 KiB
Caddy Static File MIME Type Fix (Jul 12, 2026)
Problem
The ops portal at ops.itpropartner.com had JS and CSS files served with wrong MIME types (application/json instead of text/javascript / text/css). Browsers enforce correct MIME types for scripts and stylesheets — any JS file served as application/json is silently rejected. The entire Ops namespace never loaded, login overlays stayed visible, the mobile hamburger menu never worked, and pages appeared broken.
Root cause was twofold:
1. File permissions (the real fix)
Caddy runs as the caddy system user. Files in /opt/ops-portal/static/ were created by root with chmod 600 — Caddy returned 403 Forbidden for all static file requests. Even though a Caddy handle_path block was configured, the file_server handler couldn't read the files and returned 403.
Fix: chmod 644 /opt/ops-portal/static/js/*.js /opt/ops-portal/static/css/*.css
2. Caddy handle_path nesting error (compounding factor)
The /js/* and /css/* handle_path blocks were initially nested INSIDE the /data/* handler block due to a sed command error. They only triggered when the URL started with /data/, so /js/app.js never matched.
Fix: Extract to the top level of the Caddy site block, before reverse_proxy:
ops.itpropartner.com {
handle_path /data/* {
root * /var/www/ops/data/
file_server
}
handle_path /js/* {
root * /opt/ops-portal/static/js
file_server
}
handle_path /css/* {
root * /opt/ops-portal/static/css
file_server
}
reverse_proxy 127.0.0.1:8090
}
3. Backend media_type workaround (not needed)
The FastAPI backend has an @app.get("/js/{file_path:path}") route using FileResponse. Even with media_type="application/javascript" explicitly set, the response still returned Content-Type: application/json for unknown reasons (possibly a FastAPI 0.115.6 quirk, or the media_type parameter being ignored in certain response scenarios). The Caddy static file approach bypasses the backend entirely.
Detection
- Curl the JS URL:
curl -sI https://ops.itpropartner.com/js/app.js— checkcontent-typeheader - Expected:
text/javascript; charset=utf-8 - Broken:
application/json(backend) ortext/html(caddy error page) or empty (403/404) - Browser symptom: Pages load but no JS executes — login overlay stays visible, nav not working, "Ops is not defined" errors in console
Prevention
- Always
chmod 644new static files added to/opt/ops-portal/static/ - When adding a new Caddy
handle_pathblock, verify it's at the TOP LEVEL of the site block, not indented inside another handler - After ANY Caddy config change:
caddy fmt --overwrite /etc/caddy/Caddyfile && caddy validate --config /etc/caddy/Caddyfile && systemctl reload caddy