You open the media library in wp-admin, expecting a wall of thumbnails, and instead you see a grid of grey tiles, broken-image icons, or generic file placeholders. The files are still there on the server. The library entries are still in the database. Something between the two is broken, and WordPress is not telling you what.
The reason it is hard to diagnose is that the media library is not one system. It is a database table, a folder of files, an image library on the PHP side, a JavaScript grid that loads thumbnails over HTTP, and a serialized post-meta blob that ties them together. Any one of those can fail in a way that produces the same symptom in the grid view, and the right fix depends on which layer broke. This article walks the seven causes I see in real incidents, in order of how often I find them, with a non-destructive check for each before you reach for the regenerate-thumbnails button.
What the media library shows vs. what is on disk
Before you change anything, you need to know which side of the relationship is broken. The media library grid view does three things on every page load: it queries wp_posts for rows where post_type = 'attachment', reads the _wp_attached_file post meta to find each file's relative path, and asks the browser to load the thumbnail URL built from that path. If any one of those steps returns nothing useful, the grid shows a placeholder.
Two checks tell you which layer failed. First, click an attachment to open its details panel. If you see a working preview there but a broken tile in the grid, the file exists and the URL works, which means the grid view's thumbnail URL is the problem (usually mixed content, the -scaled trap, or a missing sub-size). Second, open the WordPress Site Health screen under Tools: Site Health: Info: Media Handling. That panel reports which image library is active, which formats it supports, and the uploads path WordPress is using. Both checks together rule out three or four causes in under a minute.
If neither the grid view nor the details panel shows the image, but the file is still on disk under wp-content/uploads/2026/04/, the link between the database and the file is broken: the post meta points to the wrong path, the permissions block the web server from reading the file, or the URL the database hands the browser is no longer reachable.
Cause 1: file permissions on wp-content/uploads
This is the most common cause on hosts that have changed ownership or where someone manually copied files in over SFTP. The file exists. WordPress knows about it. But the web server user (www-data, apache, or nginx, depending on the stack) cannot read it, so the browser gets a 403 or a connection drop where the JPEG should be.
The canonical permission scheme is documented on developer.wordpress.org under file permissions: directories should be 755 (or 750), files should be 644 (or 640), and wp-config.php should be 440 or 400. The same page is explicit that 777 is a security hazard and not the fix anyone should reach for. The full procedure for setting and verifying permissions across wp-content lives in my WordPress file permissions article.
For the media library specifically, the diagnostic is small. Open the broken thumbnail URL directly in a new browser tab (right-click the placeholder, copy image URL, paste). You will see one of three things:
- A
403 Forbiddenconfirms a permission or ownership problem. The web server user cannot read the file. - A
404 Not Foundmeans the file is not where the database says it is. Skip to cause 3. - The image loads correctly. The file is fine; the grid view is failing for a different reason. Skip to cause 4 or 6.
If you saw the 403, log into SFTP or your host's file manager and check the owner and permissions on wp-content/uploads. The owner should match the user PHP-FPM runs as (visible at the bottom of the Site Health "Server" panel). You will know the fix worked when the broken thumbnail URL loads as a 200 in the network tab and the grid view repaints on the next page load.
Cause 2: GD or Imagick is missing or cannot decode the format
Site Health is the authoritative source for which image library WordPress is using. Open Tools: Site Health: Info: Media Handling and look at the active editor. WordPress prefers Imagick over GD, but on shared hosts where Imagick is disabled it falls back to GD silently. The same panel lists the formats the active library supports.
This matters because thumbnail generation happens at upload time, not on the fly. If you upload an AVIF file to a WordPress 6.5 site whose GD was compiled without AVIF support, WordPress accepts the upload, stores the file, but creates no sub-sizes. The result is a media library entry with an original_image and zero thumbnails, which the grid view renders as a placeholder.
Format support by version, verified against the WordPress core dev posts:
- WebP: native upload and sub-size generation since WordPress 5.8. The active image library still has to support WebP, which Imagick has had for years and GD has had since PHP 5.4 (compile flag
--with-webp). Lossless WebP encoding in GD requires PHP 8.1. - AVIF: native upload and sub-size generation since WordPress 6.5 "Regina", released April 2, 2024. For GD, AVIF support requires PHP 8.1+ compiled with
--with-avifand libavif 0.8.2 or later. For Imagick, a recent ImageMagick build with the AVIF codec. - HEIC: automatic conversion to JPEG since WordPress 6.7. Requires Imagick with HEIC support; GD does not handle HEIC at all.
If Site Health shows your active library does not list the format you uploaded, the fix is on the server side, not in WordPress. On managed hosting, open a ticket asking for the codec to be enabled. On a self-managed VPS, install the right system library (libavif, libheif) and rebuild the PHP extension or switch the active editor. You will know the fix held when Site Health lists the format under Media Handling and a fresh upload of that format generates sub-sizes visible in the grid view.
A second variant of this cause is Imagick installed but locked down by ImageMagick's policy.xml, which silently refuses to process certain formats. On a shared host you cannot edit policy.xml directly, but you can verify by uploading a known-good JPEG and watching whether sub-sizes are generated. If they are, Imagick is alive and the problem is format-specific.
Cause 3: orphaned post meta after a migration
When you migrate a WordPress site between hosts and copy wp-content/uploads separately from the database, it is easy to end up with mismatched paths. The database thinks the file is at 2026/03/photo.jpg, but the file actually landed at 2026/04/photo.jpg, or in a subdirectory the migration tool added, or with a different filename because the destination filesystem is case-sensitive and the source was not.
The authoritative record is the _wp_attached_file post meta key on each attachment row in wp_postmeta. It stores the path relative to the uploads base, not the full URL. WordPress combines that value with wp_upload_dir()['baseurl'] at runtime to build the thumbnail URL. If the relative path no longer matches a real file, the grid view shows a placeholder and the attachment details page shows a broken preview.
To check one specific attachment, open its edit page in wp-admin and look at the URL under "File URL". Visit that URL in a new tab. If you get a 404, the post meta is wrong or the file is missing. To check at scale, run WP-CLI:
# WP-CLI 2.10, run from the WordPress root
wp post meta list --post_type=attachment --keys=_wp_attached_file --format=csv \
| head -20
The expected output is a CSV with one row per attachment, each row showing the relative path the database has on file. Spot-check half a dozen against ls wp-content/uploads/2026/04/ and you will see the mismatch immediately.
The fix when the files are in the wrong folder is to move them to where the database expects them. The fix when the database is wrong is a search-replace on _wp_attached_file values, which is the same tooling I cover in my WordPress search-replace database URL article. Use wp search-replace rather than a SQL REPLACE, because the post meta values are stored inside serialized arrays in the related _wp_attachment_metadata key and a naive SQL update will corrupt the serialization.
You will know the fix held when the broken thumbnail URLs return 200, the grid view repaints, and wp media regenerate --only-missing (covered in cause 4) reports zero failures.
Cause 4: theme change without thumbnail regeneration
WordPress generates image sub-sizes at upload time based on whatever sizes are registered when the upload happens. The function that registers a size, add_image_size(), only defines a size; it does not produce any files for media that already exists. If you switch from a theme that registered a featured-large at 1200x600 to one that expects card-hero at 800x450, none of your existing attachments have a card-hero sub-size on disk. The grid view shows the missing sub-size as a placeholder and falls back to whatever it can find.
The diagnosis is to look inside the _wp_attachment_metadata array for a single attachment and check the sizes keys against the sizes the active theme expects. The fix is to regenerate sub-sizes for the existing media.
The most accessible tool is the Regenerate Thumbnails plugin. Install it from Plugins: Add New, activate it, then go to Tools: Regenerate Thumbnails. Tick "Skip regenerating existing correctly-sized thumbnails" and click the regenerate button. The plugin shows a progress bar and a per-attachment log.
Both this plugin and its CLI equivalent have the same hard limits worth knowing before you run them: they regenerate from the existing original file, so if the original is missing they fail; they cannot fix database corruption (cause 3); they cannot fix permissions (cause 1); and they cannot fix mixed content (cause 5). Fix the underlying cause first, then regenerate.
If you have SSH access, the wp media regenerate WP-CLI command with the --only-missing flag does the same thing from the command line and is faster on sites with thousands of attachments:
# WP-CLI 2.10
wp media regenerate --only-missing --yes
Expected output is one line per attachment, ending with Success: Regenerated X of Y images.. The --only-missing flag is the difference between a five-minute regeneration and a five-hour one on a large media library.
You will know the fix held when the grid view shows thumbnails again and the regeneration run reports zero failures.
Cause 5: HTTP image URLs after an SSL migration
If you moved a site from http:// to https:// and the media library now shows blank tiles in the grid view but not in the attachment details view, the cause is mixed content. WordPress builds thumbnail URLs from siteurl and home in wp_options. If those values still start with http:// after the SSL switch, the JavaScript grid view loads thumbnails over HTTP from an HTTPS admin page, and modern browsers silently block the requests as mixed content. The details view often works because it builds URLs differently and may use protocol-relative paths.
The two failure modes documented on WordPress core trac as #33092 and #34109 confirm the split between grid and list view: with FORCE_SSL_ADMIN set but siteurl still HTTP, the AJAX response that populates the grid hands back HTTP URLs that the page rejects.
The diagnosis takes two steps. First, in Settings: General, check that both WordPress Address (URL) and Site Address (URL) start with https://. If they do not, fix that first. Second, open the broken grid view, F12 to open developer tools, switch to the Console tab, and look for messages like "Mixed Content: The page at 'https://yoursite.nl/wp-admin/upload.php' was loaded over HTTPS, but requested an insecure element". Each blocked request is a thumbnail.
The fix has two parts. Update siteurl and home to HTTPS in Settings: General. Then run a search-replace across the database to catch any remaining http://yoursite.nl references inside post content, post meta, and serialized options. The procedure is in my WordPress search-replace database URL article, and the broader topic of mixed content beyond the media library is covered in my mixed content warnings in WordPress article.
You will know the fix held when the grid view repaints with thumbnails, the browser console no longer reports mixed-content blocks for image requests, and a fresh upload immediately appears in the grid.
Cause 6: the Big Image Threshold and the -scaled trap
WordPress 5.3 introduced the big_image_size_threshold filter, which defaults to 2560 pixels on the longest side. When you upload an image larger than that threshold, WordPress scales it down and saves the scaled copy with a -scaled suffix appended to the filename. The -scaled file becomes the new "full" size that wp_get_attachment_image_src('full') returns. The original unscaled file is preserved on disk, and its filename is stored in the original_image key of the _wp_attachment_metadata array.
To be precise about what changed in 5.3: the wp_get_attachment_image_src() function signature did not change. What changed is that the URL it returns for the "full" size now points at the -scaled copy when the upload exceeded the threshold. New helpers wp_get_original_image_path() and wp_get_original_image_url() expose the original.
The trap for the media library is the migration scenario. Imagine a site uploaded a 4000x3000 photo before WP 5.3, then upgraded to 5.3 or later. The database and the disk both reflect the pre-5.3 layout: one file, no -scaled copy. New uploads after the upgrade follow the new layout. If a server migration then partially moves files, or a backup tool restores some files but not others, you can end up with database entries that expect a -scaled file that does not exist on disk, or that expect an original file that the migration tool only copied as the scaled version.
The diagnosis starts in wp-admin. Pick a broken attachment and open its edit page. The "File URL" field shows the path WordPress has on record. Open that URL in a new tab. If you get a 404, note whether the filename contains -scaled. Then check the actual files on disk using your hosting panel's file manager or SFTP: navigate to the corresponding folder under wp-content/uploads/ and see whether the -scaled file exists, the original exists, or both. A mismatch between what the database expects and what is on disk is the cause. If you have SSH access, wp post meta get <id> _wp_attachment_metadata shows the full metadata including the file, original_image, and sizes entries.
The fix is rarely "delete the -scaled file"; that breaks the full size for new attachments. The right move is to regenerate sub-sizes (using the Regenerate Thumbnails plugin from cause 4, or wp media regenerate --only-missing if you have WP-CLI) so that the -scaled copy is rebuilt from whichever original is present, and to check that the post meta agrees with the disk. If you genuinely want to disable the threshold for new uploads on a site that handles its own scaling, return zero from the filter:
// wp-content/mu-plugins/disable-big-image-threshold.php
add_filter( 'big_image_size_threshold', '__return_zero' );
Putting it in mu-plugins means a theme or plugin update cannot overwrite it. Create the file using your hosting panel's file manager or SFTP. Test it first on staging; disabling the threshold means full-size originals are served from <img> tags everywhere, which inflates pages on image-heavy templates.
You will know the fix held when the grid view shows thumbnails for both old and new attachments and a regeneration run (via the Regenerate Thumbnails plugin or wp media regenerate --only-missing) completes without failures.
Cause 7: WebP, AVIF, or HEIC uploaded to a server that cannot decode them
This is a narrower variant of cause 2 and worth calling out separately because the symptom looks identical to a permission problem. You upload a .webp from a designer or a .heic straight from an iPhone, the upload appears to succeed, the attachment exists in the library, but the thumbnail is a broken icon and the details view also fails to render a preview.
The cause is that WordPress 5.8 added native WebP, 6.5 added AVIF, and 6.7 added automatic HEIC-to-JPEG conversion, but each one depends on the active image library on the server. The dev posts on WebP, AVIF, and HEIC make the dependency explicit. If GD on PHP 8.0 is your active editor, AVIF will not work at all, because the GD AVIF functions arrived in PHP 8.1. If Imagick is your active editor but the host's ImageMagick was compiled without HEIC, HEIC uploads will not be converted.
The diagnosis is the same as cause 2: open Site Health under Tools: Site Health: Info: Media Handling and check whether the format you uploaded is in the supported list for the active editor. If it is not, the file is on disk but WordPress could not generate sub-sizes for it, and the grid view falls back to a placeholder.
The fix is server-side: enable the codec, switch image library, or convert the file to a format WordPress can handle (JPEG, PNG, WebP) before upload. On managed hosting, ask the host to confirm AVIF or HEIC support and enable it if needed. You will know the fix held when Site Health lists the format under the active editor and a re-upload generates sub-sizes that show in the grid view.
When to escalate
If you have worked through causes 1 through 7 and the media library still shows blank tiles, gather the following before opening a support ticket or calling in a developer. The first thing anyone experienced will ask for is exactly this list:
- Your WordPress version, PHP version, and the active image editor and supported formats from Site Health: Info: Media Handling.
- A specific broken attachment ID, the value of its
_wp_attached_filepost meta, and the actual file path on disk for the same attachment. - The HTTP status code returned when you load the broken thumbnail URL directly in a new browser tab (
200,403,404, or other). - The browser console output from the media library page, including any mixed-content warnings.
- Whether the site was recently migrated, switched to HTTPS, had its theme changed, or had
wp-content/uploadstouched manually over SFTP. - Output of
wp media regenerate --only-missing --dry-runif you have WP-CLI access, which lists exactly which sub-sizes are missing per attachment. - The full
_wp_attachment_metadatavalue for one broken attachment (wp post meta get <id> _wp_attachment_metadata).
Send all of that in the first message. It routes the ticket to the right engineer without a round trip.
If the same root cause is producing other media-related failures, two adjacent articles cover specific facets of the same problem space: the HTTP error during media upload article covers uploads that fail before the file ever lands in the library, and the mixed content warnings article covers the SSL side beyond the media library specifically.
How to prevent it from coming back
Most of the seven causes share a common thread: they show up after a change that touched files, paths, or URLs. Three habits prevent the bulk of them.
- Migrate
wp-content/uploadsand the database in one operation. Tools like WP Migrate or the All-in-One WP Migration plugin keep the post meta and the file paths consistent on the destination. Manual SFTP plus a SQL dump is where most cause-3 incidents come from. - Switch to HTTPS in one operation, including a search-replace. Toggling
siteurlto HTTPS is the easy half. Running a database-wide search-replace forhttp://yoursite.nlis the half people skip, and it is the half that causes the grid-view-only mixed content bug. - Verify Site Health after every PHP or image-library upgrade. A PHP minor version bump can drop a codec the previous build had. If your site relies on AVIF or HEIC, Tools: Site Health: Info: Media Handling is the one panel worth checking after every host-level change.