Your gallery shows grey boxes, a small broken-picture icon, or nothing at all where the photos used to be. The photos are usually still fine; what breaks is the address the page asks for, or the step that is meant to load it. One check on a single broken image tells you which. Below is a rebuilt example (not a real client), that check, and the fix for each answer.
https://? An old domain, a staging address or http:// is already the answer.404 (missing), 403 (refused), (blocked:mixed-content), or a red row naming another site. Pictures with no row at all were never requested, which points at a lazy-load script. The Console tab names any script error.https://, posts still point at the old addresses, because WordPress stores the full address of every image inside the post text. Fix it with a search-and-replace across the database (the Better Search Replace plugin, or wp search-replace if you have WP-CLI): old address to new, a dry run first, and a backup before the real run. Do not edit the database by hand with a text editor: some values carry their own length, and a plain edit breaks them.wp-content/uploads folder, or only part of it. The Media Library still lists the photos (that list lives in the database) but every one is a 404. Copy the uploads folder again from the old host or the backup, keeping the year and month folders as they were.Shop.JPG and shop.jpg as the same file; Linux servers do not. After a move from one to the other, the images whose names differ only in case go missing. Rename the files to match the addresses, or update the addresses.wp-content/uploads were changed (a security plugin, a host move, a hand-made change). Folders should normally be 755 and files 644; many hosts have a "fix permissions" button. A stray .htaccess inside uploads that denies everything does the same.example.com also blocks www.example.com, your CDN, or Google Images. Add every version of your own address, or switch it off to test..webp copy. If that copy was never made, or the account ran out, the swapped image fails while the original file is fine. Switch the plugin's CDN or WebP rewrite off; if the photos come back, regenerate its copies or contact the plugin's support.data-src) and a script moves it in when you scroll. If that script is broken, delayed or loaded twice (the theme, a speed plugin and WordPress all lazy-load), the swap never happens and you see placeholders. Turn off lazy loading and any "delay JavaScript" option in your speed plugin, one at a time, until the photos appear; keep one lazy loader, not two.| What you see | Usual cause | Usual fix |
|---|---|---|
Image address names an old domain, a staging site or http:// | Site moved, addresses did not | Database search-and-replace, dry run and backup first |
| Image opens on its own as "Not Found" | Uploads not copied, or a name changed case | Copy wp-content/uploads again; match file names |
| "Forbidden" or 403 on every image | Folder permissions or hotlink protection | Folders 755, files 644; add your own addresses to the hotlink list |
Address ends in .webp or points at a plugin's server | CDN or WebP rewrite with no copy behind it | Switch the rewrite off, regenerate the copies |
| Grey placeholders, no image requests in the Network tab | Lazy-load script broken or doubled | One lazy loader; switch off "delay JavaScript" to test |
| Full-size photo fine, small versions missing | Thumbnails never generated | Regenerate Thumbnails; ask the host about GD or Imagick |
| Photos fine for you, broken for visitors | An old cached page with old addresses | Clear the caches, then check in a private window |
Once one broken image has told you its status, the fix is usually minutes. If you would rather hand it over, or the photos still will not show, that is what the box below is for. Related: a photo that will not upload, a site slow because of big photos and changes that do not show on the live site (an old cached page can carry old image addresses too).