<

[Simplified] How to deploy React app to AWS S3 without cache problems?

2024-01-11

Last year I wrote a blog about the same top here.

In the previous blog, I need to:

  1. build the app normally;

  2. manually move the built static assets (js/css/images etc.) to a versioned folder; 

  3. manually replace the path in index.html and manifest.json to add the versioned prefix;

  4. upload everything to S3;

But yesterday I found that create-react-app has a native support for placing assets to a specific folder, and all the links in index.html will also have that prefix automatically. And since I now generate all my favicon with Favicon37, I don't need to care about manifest.json anymore.

The trick is the PUBLIC_URL env variable:

1 Create a timestamp same as before:

const timestamp = new Date()
  .toISOString()
  .replace(/[^0-9]/g, '')
  .slice(0, 14);

2 Update the PUBLIC_URL in .env.production to be the timestamp:

const envPath = path.join(__dirname, '..', '.env.production'); // Adjust the path to your .env file
let envContents = fs.readFileSync(envPath, 'utf-8');
let lines = envContents.split('\n');

let found = false;
lines = lines.map(line => {
  const [currentKey] = line.split('=');
  if (currentKey === key) {
    found = true;
    return `${key}=${value}`;
  }
  return line;
});

if (!found) {
  lines.push(`${key}=${value}`);
}

// Filter out any empty lines
lines = lines.filter(line => line.trim() !== '');

fs.writeFileSync(envPath, lines.join('\n'));

3 Build the app:

console.log('Building the app...');
execSync(`npm run build`);
console.log('Build app completed.');

4 Upload assets and index.html separately, so they have different cache-control:

console.log('Uploading assets to S3...');
execSync(
  `aws s3 sync build/static ${process.env.S3_URL}/${timestamp}/static --cache-control max-age=31536000,public`
);
console.log('Upload assets to S3 completed.');

console.log('Uploading index.html to S3...');
execSync(
  `aws s3 cp build/index.html ${process.env.S3_URL}/index.html --cache-control max-age=0,no-store`
);
console.log('Upload index.html to S3 completed.');

5 Remove older versions from S3, you can check the old blog for this step.

That's it.