docs/self-hosting/guides/cdn-caching.mdx
This guide explains a common issue with frontend asset caching during deployments and how to solve it using a CDN.
Modern frontend build tools like Vite generate content-hashed filenames for static assets (e.g., main-abc123.js). Each build produces unique filenames based on file contents. During deployments, this can cause a race condition:
index.html which references main-abc123.jsmain-xyz789.js (new build)main-abc123.js from cached HTMLThis results in broken pages, failed SPA navigation, and requires users to manually refresh.
<Note> This is a documented limitation in Vite's official guidance: [Load Error Handling](https://vite.dev/guide/build#load-error-handling) </Note>Infisical includes a built-in workaround that detects version mismatches and triggers a page reload. While functional, this introduces a noticeable delay for users during deployments.
The solution is to store static assets externally (e.g., S3, GCS, Azure Blob) and serve them through a CDN (e.g., CloudFront, Cloud CDN, Cloudflare). Assets are uploaded before container deployment, ensuring old versions remain available.
flowchart LR
User[User Browser]
CDN[CDN]
S3[(Object Storage)]
App[Your Infrastructure]
User --> CDN
CDN -->|"/assets/*"| S3
CDN -->|"/* (default)"| App
The key points:
At Infisical, we use CloudFront + S3 for this purpose, but you can use any CDN and object storage combination that fits your infrastructure.
Infisical provides a built-in command to export frontend assets from the Docker image:
# Export as tar archive to stdout
docker run --rm infisical/infisical npm run --silent assets:export > assets.tar
# Extract the archive
tar -xf assets.tar
ls assets/ # Content-hashed JS/CSS files
Or export directly to a mounted directory:
docker run --rm -v $(pwd)/cdn-assets:/output \
infisical/infisical npm run --silent assets:export /output
The command exports the /assets directory containing:
main-abc123.js, chunk-def456.js)styles-789xyz.css)These files are safe to cache with long TTLs because their filenames change whenever the content changes.
The general deployment flow should be:
npm run assets:export# Example: Export and upload to S3
docker run --rm infisical/infisical:$VERSION npm run --silent assets:export > assets.tar
tar -xf assets.tar
aws s3 sync assets s3://your-bucket/assets --cache-control "public, max-age=2592000"
# Then deploy your container