Overview

Each deployment creates a new folder in the S3 bucket. The folder name corresponds to the Git commit short SHA or tag name depending on environment.


Bucket Structure

DEV Environment

bucket-name/
├── index.html                    ← Active version (root)
├── <COMMIT_SHA_OR_TAG>/          ← Deployment 1
│   ├── index.html
│   └── assets/
│       ├── index-a1b2c3d4.js
│       ├── index-e5f6g7h8.css
│       └── logo-i9j0k1l2.png
├── <COMMIT_SHA_OR_TAG>/          ← Deployment 2
│   ├── index.html
│   └── assets/
│       ├── index-m3n4o5p6.js
│       ├── index-q7r8s9t0.css
│       └── logo-u1v2w3x4.png
└── <COMMIT_SHA_OR_TAG>/          ← Deployment 3
    ├── index.html
    └── assets/
        ├── index-y5z6a7b8.js
        ├── index-c9d0e1f2.css
        └── logo-g3h4i5j6.png

How it works

The root index.html always points to the active version. Each deployment’s assets are isolated in their own folder — this enables instant switching between versions.


Vite Config

Dynamic base path ensures assets are referenced correctly from each subfolder:

// https://vite.dev/config/
export default defineConfig(({ mode }) => {
  const env = loadEnv(mode, process.cwd());
 
  return {
    plugins: [react(), tailwindcss()],
    resolve: {
      alias: {
        '@': path.resolve(__dirname, './src'),
      },
    },
    base: env.VITE_RELEASE_PATH ? `/${env.VITE_RELEASE_PATH}/` : '/',
  };
});

VITE_RELEASE_PATH

This variable sets the deployment subfolder (commit SHA or tag). If unset, it defaults to root /. This way each folder’s index.html references its own assets via the correct relative path.


Rollback Strategy

Step 1 — Copy Previous Version to Root

Identify the commit SHA or tag you want to roll back to, then copy its index.html to the bucket root:

aws s3 cp s3://$S3_BUCKET_NAME/<COMMIT_SHA_OR_TAG>/index.html \
  s3://$S3_BUCKET_NAME/index.html

Step 2 — Invalidate CloudFront Cache

Force CloudFront to serve the rolled-back version immediately:

aws cloudfront create-invalidation \
  --distribution-id $CLOUDFRONT_DISTRIBUTION_ID \
  --paths "/index.html"

Note

Invalidation is only needed for index.html since asset files have unique hashes in their filenames and are cached indefinitely.


Why This Works

flowchart LR
    A[CloudFront] -->|serves| B["/index.html (root)"]
    B -->|references| C["/abc123/assets/*"]
    D["Rollback: copy old index.html to root"] -.->|now references| E["/def456/assets/*"]

The root index.html is the only file that needs to be replaced. It contains references to assets in the corresponding subfolder (thanks to VITE_RELEASE_PATH in the Vite config). Old assets remain in place — switching happens instantly.