Six blog posts on my portfolio had broken cover images for longer than I'd like to admit. The images were in S3. The management command to restore them had been written and run. And yet — nothing. Blank covers, every time.

Here's the full breakdown of what went wrong, why, and how it got fixed.

The Setup

My portfolio backend is a Cookiecutter Django project running on a DigitalOcean droplet with Docker Compose. Blog post cover images are stored on AWS S3. The cover field on the BlogPost model is an ImageField that stores a relative path like blog_posts/deploy.webp — Django's S3 storage backend handles prepending the media/ prefix and building the full URL.

When I migrated away from using Hashnode as a headless CMS and imported all posts into my own Django backend, the cover images came along as CUID-based filenames (e.g. blog_posts/cmoxrumae00ms2em7bje5at07.png). Those CUIDs don't exist in S3 — the actual files were uploaded separately with descriptive names like deploy.webp, manual.webp, sortinghashnode.webp.