On adult portals, images are the traffic nobody plans for. Video gets the architecture review; images quietly become the majority of requests, the majority of cache objects, and the first thing to break under a catalogue migration. The failure is rarely bandwidth — it is object count, cache churn, and a shared cache path that lets tiny images evict expensive video.
The mechanism
A catalogue of a million titles produces several images each: posters, grid thumbnails, hero images, hover sprites, and preview clips. Public traces of a large adult portal show images were roughly 80% of requests against roughly 37% of bytes, while video was roughly 20% of requests against roughly 63% of bytes. Images are cheap per byte and expensive per object. Every derived size is a distinct cache object, so a single source image can become ten objects across form factors and breakpoints. The result is enormous object cardinality with tiny average size.
Cause 1: inode and object-count exhaustion
Storage backends fail on object count before capacity. A filesystem can run out of inodes (df -i shows 100% used) while df -h shows free space; object storage has per-bucket request-rate and key-count considerations; and a cache tier has a fixed maximum number of entries regardless of their size. A catalogue of many small derivatives is exactly the workload that hits these limits. Track inode/object count and cache entry count as first-class capacity metrics, not afterthoughts.
Mitigations:
- Shard keys across multiple buckets or prefixes to spread request rate.
- Generate sizes on demand from a small set of source images instead of storing every variant.
- Consolidate sprites so a grid of previews is one object rather than twenty.
- Prune unused derivatives with the same lineage discipline used for takedowns.
Cause 2: hover sprites and previews
Hover previews and animated thumbnails are the highest-cardinality, highest-request image objects on many sites. A preview clip per title can double or triple the object count. Treat them as a distinct class with their own cache policy and, where possible, serve them from a sprite sheet or a short low-bitrate video rather than N separate images. Because they are requested on hover, they amplify cache churn disproportionately to their byte size.
Cause 3: image optimization done badly
Naive image optimization re-encodes on every request or stores a combinatorial matrix of size × format × quality. Both scale badly. Good practice:
- Negotiate format at the edge from
Accept, serving modern formats where supported and falling back otherwise. - Cap the variant matrix — a defined set of sizes and one or two formats, not the cross-product of every device hint.
- Cache transformed output keyed by source plus transform parameters, so a transform is computed once.
- Strip metadata that is not needed for delivery, and never expose original uploads.
For adult platforms there is an additional consideration: originals can carry sensitive metadata (location, device, and sometimes identity). Strip it on ingest, and never make the original publicly addressable.
Cause 4: image and video share a cache path
If thumbnails and video segments share one cache tier and key space, a burst of image requests evicts video bytes that are expensive to refill, and vice versa. Separate the planes:
- Different cache paths or resources for image and video, so eviction is isolated.
- Different freshness: images are immutable once published, so they can be cached very aggressively; manifests and API responses are not. See Cache control and cache keys.
- Different cache keys: images keyed by source and transform, video by object and range.
Immutable image URLs (content-addressed or versioned) let you cache images for a long time and change them by changing the URL, which also makes takedowns and corrections clean.
What good looks like
- Object/inode count and cache entry count are monitored capacity metrics.
- A bounded variant matrix with edge format negotiation.
- Hover previews consolidated into sprites or short clips.
- Image and video caches separated, with image freshness long and immutable.
- Originals stripped of metadata and never public.
How to prove it
Split image and video cache metrics on one hostname, then route the image plane through a tuned path and compare image byte offload, origin requests, cache entry churn, and p95 image latency against the incumbent. This is a controlled test that leaves video delivery untouched.
Related reading
- CDN overview for resource separation.
- Cache control and cache keys for immutable-URL caching.
- Cache hit ratio is lying to you for why image request share misleads.