Fixing S3 Security (and Why It Was Worth It)

Migrated the app from public S3 assets to IAM-scoped private storage using presigned URLs and centralized helpers.

I set out to lock down a public S3 bucket that had slowly turned into the backbone for half the site. The old setup worked, but it was certainly not the best way to do it. Hardcoded URLs everywhere, no real access control, and no clean way to scale or rotate credentials. So I flipped the switch and made everything private. Games and documents survived because they already used presigned URLs through a centralized S3 service. Everything else did not. About page, blog, projects, branding, navbar logo, and a few other images across the site suddenly went away. It exposed a pile of technical debt that had been hiding in plain sight, and fixing it meant routing every image through the same helper pattern, removing hardcoded URLs, and letting IAM do its job instead of relying on public access. It was messy, and definitely involved more talking with Chat than I care to admit, but the result is worth it. Assets are private, access is scoped, URLs expire, and the system is finally consistent. Next time I need to tighten security or move things around, it will be boring instead of confusing.

Posted: January 18, 2026