Every WordPress to Astro move I do has the same quiet trap. The pages get rebuilt, the redirects are mapped, DNS is sorted, and then a month later the old host gets cancelled and half the images on the new site break. Why? Because they were still being loaded from oldsite.com/wp-content/uploads/....
The media library is the part people forget. Here is how I handle it now.
1. Get the originals, not the thumbnails
WordPress makes 5 to 10 resized copies of every upload (photo-300x200.jpg, photo-1024x683.jpg, etc). If you scrape the live site you mostly get those. You want the originals.
Easiest ways:
- SFTP or the host's file manager: zip up
wp-content/uploadsand download it. - No server access? Tools > Export gives you a WXR file with every attachment URL. I parse that and pull each
wp:attachment_url. - Or WP-CLI if it's there:
wp media list --fields=url --format=csv.
Then I throw away the sized copies:
find uploads -regextype posix-extended -regex '.*-[0-9]+x[0-9]+\.(jpe?g|png|webp)$' -delete What's left is usually way smaller than the client expects. Lots of sites have 4GB of uploads and 300MB of actual originals.
2. Only keep what's actually used
I grep the exported content (and the rebuilt pages) for filenames and only bring across files that are referenced. The rest goes in an archive folder for the client. Nobody needs 2016's Christmas party photos shipping with the site.
3. Rename to something sensible
WordPress libraries are full of IMG_4821-scaled-1.jpg and Screen-Shot-2019...png. While I'm in there I rename to lowercase, hyphenated, descriptive names: team-sarah-office.jpg, kitchen-reno-before.jpg. A small script with a CSV mapping old name to new name does it, and I keep that CSV because I need it in step 6.
4. Fix the bloated stuff
Before anything goes in the repo:
- Anything over ~500KB gets a look.
- PNG photos (very common, clients upload screenshots of photos) get converted to JPG or WebP. I've seen 6MB PNG hero images go to 180KB.
- Anything wider than about 2400px gets scaled down. The original camera file isn't needed on the web.
sharp or squoosh-cli in a loop handles this fine.
5. Put them in the right place in Astro
My rule:
-
src/assets/for images used in pages and components. Astro's<Image />and<Picture />then generate the sizes, srcset and modern formats at build time, so I'm not hand making thumbnails like WordPress did. -
public/for things that need a fixed, stable URL: PDFs, downloadable price lists, menus, the odd image linked from emails or Google.
--- import { Picture } from 'astro:assets'; import hero from '../assets/kitchen-reno-after.jpg'; --- <Picture src={hero} formats={['avif', 'webp']} widths={[480, 960, 1600]} sizes="(max-width: 960px) 100vw, 960px" alt="Finished kitchen renovation" /> Always fill in the alt text properly. The WordPress alt fields are usually empty or say "image1", so this is a good chance to fix it.
6. Don't let old URLs 404
PDFs are the big one. Clients have linked /wp-content/uploads/2021/03/Menu.pdf in emails, Google Business profiles and other peoples sites for years. Same with images that ranked in Google Images.
Using the rename CSV from step 3 I generate redirect lines:
/wp-content/uploads/2021/03/Menu.pdf /files/menu.pdf 301 /wp-content/uploads/2020/11/IMG_4821-scaled.jpg /images/kitchen-reno-before.jpg 301 On novence.ai that just goes in a /_redirects file in the project, and it ships with npx novence deploy like everything else. I only redirect the files that had real traffic or backlinks, not all 3000.
7. Make sure nothing still points at the old site
Before cutover I search the whole built output:
npm run build grep -rn "wp-content/uploads" dist/ That should return nothing. Imported blog posts are the usual culprit because the HTML still has absolute URLs to the old domain. If anything shows up, it gets fixed before launch, not after.
8. Keep the export until the old host is gone
I keep the full original uploads zip, the WXR export and the rename CSV in the client's folder until the old hosting is cancelled and the new site has run clean for a few weeks. Then it goes to cold storage. I've been saved by that zip more than once when a client says "where's that photo from the old about page".
Quick checklist
- [ ] Originals downloaded, sized copies deleted
- [ ] Unused files archived, not shipped
- [ ] Files renamed, mapping CSV saved
- [ ] Big PNGs converted, oversized images scaled
- [ ] Page images in
src/assets/, downloads inpublic/ - [ ] Redirects for PDFs and important image URLs
- [ ]
grep wp-content dist/returns nothing - [ ] Export kept until the old host is cancelled
It's not the exciting part of a migration but it's definately the part that bites you later if you skip it.




