Back to Blog
awscloudfronts3infrastructuredeployment

Frontend Devs Should Care More About AWS Than They Think

December 4, 20254 min read

Frontend Devs Should Care More About AWS Than They Think

For most of my career I thought of cloud infrastructure as somebody else's job. There was a platform team. I built the React app, pushed to main, a deploy happened. If something was on fire in the CDN, that was a Slack thread I read but didn't contribute to.

I no longer think that's a good way to be a senior frontend engineer. The "I just write the frontend" instinct is fine three years in. Around five or six it stops being fine — the bugs you can't solve are increasingly at the seam between your bundle output and whatever's serving it. For most frontend work, that seam is AWS.

The two-hour index.html

Standard CRA-style deploys: build, upload to S3, invalidate /index.html, next request picks up new HTML referencing new hashed JS. We'd done it a thousand times.

One afternoon we shipped a payment bug fix. Deploy went green. Bug still there in prod. Hard-refreshed. Gone. The build was right and the new code was in S3, but the world was getting served old HTML. For a humbling stretch we blamed browser cache and asked customers to refresh.

It wasn't browser cache. Someone had updated the CloudFront behavior for index.html to a two-hour minimum TTL weeks earlier, on the reasonable-seeming logic that it was a minimum and invalidation would override it. But CloudFront invalidations are eventually consistent — supposed to take 60 seconds, in practice much longer when many edge locations hold the path. We watched prod serve stale HTML for nearly two hours.

The lessons that stuck: cache invalidation isn't free and isn't instant. Never wildcard-invalidate (--paths "/*" is always a bad smell). Hash all your JS/CSS, set them to Cache-Control: public, max-age=31536000, immutable, and only invalidate the unhashed HTML that points to them.

The Lambda@Edge nobody remembers writing

On a different team, the Cache-Control headers from S3 were being mysteriously rewritten. Bundle uploaded with max-age=31536000, served with max-age=300. I spent half a day in S3 object metadata before a colleague casually mentioned there was a Lambda@Edge on the origin response overriding Cache-Control for that path. Written four years earlier by an engineer who'd since left, to fix a problem from a different era. It had been silently neutering caching for years. The infra team had inherited it and assumed it was doing something important.

The lesson isn't about Lambda@Edge. The request and response are touched by more things than the diagram on the wall shows. Read the CloudFront config. Ask about edge functions. Ask if there's a WAF stripping headers.

S3 in one paragraph

Make the bucket completely private. Put CloudFront in front with Origin Access Control. The bucket policy trusts only the distribution. No object ACLs, no public-bucket worries, no anxious emails from AWS. For user-uploaded files, the backend mints short-lived signed URLs. "Can I get a signed URL with a 5-minute TTL?" is the right question. "Can you make the bucket public?" is the wrong one — be the person pushing back.

The actual point

There's a category of frontend engineer very good at the framework layer — components, state, animations, design system — who stops there. They're valuable. There's another category that knows their build output well enough to argue about how it's being served: the cache, the CDN, the headers, the IAM role doing the upload. When something's wrong in production, they show up with hypotheses instead of waiting for the platform team to triage.

Senior frontend work lives in that second category. The difference is whether someone spent a couple of afternoons getting curious about the layer below their build output. You don't need to become a cloud engineer. You do need to read the CloudFront config and notice when it's wrong. That's the threshold, and crossing it changes which problems you're allowed to own.