Normal view

Anyone else's media library just... die after 10k+ files? Here's what finally fixed it for us

21 August 2026 at 08:19

Hi folks,

Been managing a bunch of client sites and hit that wall where the Media Library just stops being usable once you're past a few thousand images/videos. "Just install a caching plugin bro" doesn't really cut it at that scale. Wrote up everything that actually worked for us here, but here's the gist:

The database is probably your real bottleneck

Turns out wp_posts isn't indexed well for big media libraries by default, so admin queries crawl. Adding an index on post_type, post_status, post_date (plus one on wp_postmeta for meta lookups) made a noticeable difference for us. Also worth trimming how many image sizes your theme registers — we had way more than we needed and it was just bloating wp_postmeta for no reason.

If you're not already running Redis or Memcached for object caching, that's basically mandatory at this scale. Longer TTLs on attachment queries especially.

The admin UI itself is dumb about large libraries

You can drop the items-per-page in Media Library with a filter — we went down to 40 and it noticeably sped up load times once we crossed 10k files. There are also some plugins that do virtual scrolling so it's not trying to render your entire library at once, which helps a lot.

Offloading to S3/R2 was honestly the biggest win

If you're still storing everything locally, this is probably the single best long-term fix. We went with Cloudflare R2 mainly because of no egress fees — WP Offload Media handles the syncing and URL rewriting for you, and you can set it to nuke the local copies after upload so you're not double-storing everything.

Also — check your inodes if you're on a VPS

This one bit us before we knew to look for it. Every file = 1 inode, and once you're at 50k+ files you can run out of inodes before you even hit your storage cap. df -i will show you where you stand. If you're close, start cleaning up old revisions, unused thumbnail sizes, spam attachments, that kind of thing.

For actually organizing the chaos

Media Library Assistant is great if you need bulk editing or EXIF/IPTC stuff for photography sites. FileBird or Real Media Library are nice if scrolling through one giant flat list is making you want to throw your laptop.

Anyway, full writeup with the actual code snippets is here if you want to go deeper on any of this. Curious if anyone's dealing with even bigger libraries (20k+) — what are you doing that's more extreme than this?

submitted by /u/anouarabsslm
[link] [comments]

The mental model that finally made WordPress caching layers click for me (OPcache vs object vs page)

7 August 2026 at 18:40

I spent a while stacking caching plugins and wondering why things weren't as fast as they should be. The thing that fixed it wasn't a plugin, it was understanding that the three caching layers do completely different jobs and you build them from the bottom up.

OPcache is the foundation. It caches compiled PHP bytecode so the interpreter isn't recompiling your code on every request. It helps every single PHP app, it operates independently of everything else, and you basically never turn it off. In production I set validate_timestamps=0 and just flush it on deploy.

Object caching (Redis or Memcached) sits above that. It stores the results of expensive database queries so WordPress isn't making dozens of DB round trips per page. This is the layer that matters most for logged-in users and anything dynamic, because those requests skip page caching entirely.

Page caching (Nginx FastCGI cache for me) is the big hitter for anonymous traffic. A cached page never touches PHP or MySQL, it just serves HTML. Massive for traffic spikes, but useless for logged-in users, so you set proper bypass rules for wp-admin, carts, checkout and logged-in cookies.

The lightbulb moment was realizing they don't compete, they cover for each other. Page cache handles anonymous hits, object cache carries the cache misses and logged-in users, OPcache speeds up all the PHP underneath both. The mistakes I'd been making were running two page cache solutions at once and expecting object cache to help on pages that were already fully page-cached (it doesn't, page cache bypasses WordPress completely).

The other thing that clicked: the right strategy depends on your site. Marketing sites lean hard on page caching. Membership and ecommerce lean on object caching because everyone's logged in. Frequently updated content sites need shorter TTLs and smart invalidation.

I wrote the full guide up with the actual php.ini, wp-config and Nginx config snippets in this article Understanding WordPress Caching Layers: A Developer's Configuration Guide. Curious how you guys handle cache invalidation on fast-moving sites, that's the part I still tweak most.

submitted by /u/anouarabsslm
[link] [comments]

Caching didn’t fix our high-volume WordPress sites. Database indexing and cursor pagination did

6 August 2026 at 14:40

We manage some content-heavy WordPress sites (news portals, big blogs, 10k+ posts) and hit a wall where no amount of page or object caching helped. Turned out the bottlenecks were baked into how WordPress stores data, not something a cache layer could paper over. Sharing what actually moved the needle in case it saves someone a bad week.

Three things were doing most of the damage:

Taxonomy queries. On a site with 50k posts and ~10 tags each, wp_term_relationships balloons to half a million rows. Filtering by multiple taxonomies means expensive JOINs, and without the right indexes MySQL just falls back to full table scans. A composite index on term_taxonomy_id and object_id took some of these from seconds to milliseconds.

Post meta lookups. wp_postmeta gets brutal at scale since every custom field is its own row. Anything that filters or sorts by meta (featured status, view counts, custom dates) JOINs that table repeatedly. Indexing meta_key with a prefixed meta_value (191 chars for utf8mb4) helped a lot. For the really hot fields we ended up denormalizing into a small custom table kept in sync via save_post.

Deep pagination. WordPress uses OFFSET, so page 500 makes MySQL fetch and throw away 10,000 rows before it returns anything. Crawlers hitting deep archives were quietly hammering the DB. Switching to cursor-based pagination with date_query comparisons kept query time flat no matter how deep the page.

Query Monitor on staging plus EXPLAIN to confirm the indexes were actually being used was the workflow that tied it all together.

Happy to share the SQL and WP_Query snippets if anyone wants them, I wrote the whole thing up with code somewhere. Curious what’s worked for others too, especially anyone who’s gone the custom-table route.

submitted by /u/anouarabsslm
[link] [comments]
❌