Skip to content

Cloudflare images slow: cf-cache-status splits cache from payload

By Published 11 min read

On this page (8 sections)
  1. The short version
  2. Read cf-cache-status on the image URL, not the page
  3. What DYNAMIC or MISS on a jpg actually means
  4. What HIT but still slow means for WordPress images
  5. Compare bytes before touching any optimization setting
  6. Why purge everything can leave an image DYNAMIC again
  7. The order of work that stops you tuning the wrong thing
  8. Questions people still ask

In short: Open the image URL directly in DevTools and read cf-cache-status. DYNAMIC or MISS means Cloudflare isn't caching the file at all. HIT but still slow means the cached copy is too large or the origin response is delayed.

Part of our guide on causes of login failure

A cached image can still crawl because the CDN is serving a heavier file than the one sitting on your disk.

At a glance
First header to readcf-cache-status
Means never cachedDYNAMIC or MISS
Means cached too bigHIT but slow
Size check headercontent-length in bytes
Common mistakeTesting the page URL
Free tools neededcurl and browser devtools

The short version

  • Test the image URL, never the page URL. Page rules and bypass cookies apply to HTML, not always to the .jpg underneath it.
  • Cache status is the fork. Check it before you touch Polish, WebP or resizing settings, or you tune a file the CDN never stored.
  • Compare content-length in the response against the file size on your server. A big gap means something re-encoded the image in transit.
  • Purge-everything can drop an image back to DYNAMIC on the next request if the bypass that caused it is still in place.
  • Fix cacheability first, re-encoding second, resizing third. Work in that order and you only measure each change once.

Read cf-cache-status on the image URL, not the page

Say you test your blog post in the browser and the page loads fast. The images still feel sluggish, so you blame Cloudflare. That conclusion is premature, because the page cache and the image cache are two different things.

The header that tells you which one you are looking at is cf-cache-status on the image request itself. Open developer tools, hit the Network tab, reload, and click the .jpg or .webp row. Then read the Response Headers panel.

If you prefer the command line, run curl -I https://yoursite.com/wp-content/uploads/2025/01/photo.jpg. The -I flag asks for headers only, so you get the status line and nothing else. Look for the cf-cache-status line.

Three values matter here. HIT means Cloudflare served the file from its edge cache. MISS means it went to your origin and was not stored. DYNAMIC means Cloudflare decided this file is never cacheable at all. The other half of this decision is wordpress hosting cpanel login troubles.

  1. Open the post in a browser and press F12 to open developer tools.
  2. Click the Network tab, then tick the Images filter so only pictures show.
  3. Reload the page with Ctrl+Shift+R. This bypasses your own browser cache.
  4. Click one slow image row and open the Headers panel.
  5. Read cf-cache-status and note the value. Write it down before changing anything.
  6. Run the same URL through curl -I to confirm the reading outside the browser.

A second reading to grab in the same panel

While the Headers panel is open, find content-length. That is the size in bytes of the image Cloudflare actually served you.

Compare it against the file on your server. If the served file is larger than the original, something in the middle re-encoded it. That finding matters later, once cache status is sorted.

What DYNAMIC or MISS on a jpg actually means

Browser devtools network panel open on a JPEG request
Browser devtools network panel open on a JPEG request

Picture a reader who set a Cache Everything page rule and thinks every file is now cached. Then the image reads DYNAMIC anyway. The rule probably did not apply to it, or something cancelled it. Before you commit to anything, it is worth looking at why hero image causes lcp failure.

DYNAMIC on an image URL usually means one of four things. A bypass cookie is set on the visitor's session. A page rule has a bypass in it. The image sits on an unproxied subdomain such as cdn.yoursite.com that never goes through Cloudflare at all. Or the URL carries a query string the cache treats as a new address.

That last one catches people. WordPress plugins often append ?ver=6.7 to image or asset URLs. Text after the question mark can create a separate cache key, so a file you purged stays cold under a new address.

MISS is the softer version. The file is cacheable but was not in the cache at that moment. A purge, a low edge TTL, or a first visit from that region all produce MISS without anything being broken. Before you commit to anything, it is worth looking at slow loading websites fix.

  • Bypass cookie: logged-in WordPress sessions set wordpress_logged_in cookies that skip caching by design.
  • Page rule bypass: check Rules then Page Rules for a Cache Level set to Bypass on the path holding your images.
  • Unproxied subdomain: in your DNS list, a grey cloud next to the record means traffic never reaches Cloudflare.
  • Query string: a ?ver= suffix can act as a separate URL, so the cached copy you want is not the one being asked for.
  • Origin cache headers: Cache-Control: no-store or private from your host can keep the file out of the edge cache.
What works
  • Free to check, no settings changed
  • Separates four causes with one test
  • Explains why only logged-in views felt slow
What to watch
  • Needs access to DNS and page rules
  • Some hosts block header inspection
  • Bypass rules can hide in odd paths

What HIT but still slow means for WordPress images

Say the header says HIT and the image still takes two seconds to paint. Cloudflare is doing its job. The problem is the file it cached.

Three causes cover most cases. The origin file is oversized, often a 3000-pixel-wide upload shown in a 700-pixel column. Your theme is not emitting smaller srcset candidates, so every screen downloads the full file. Or the image optimizer re-encoded the picture into something larger than the original.

That third one surprises people. Convert a small JPEG to WebP and the result is normally smaller. Convert an already-compressed image, or serve WebP at a quality setting near 100, and the new file can be heavier than the file it replaced. We cover how to monitor spam traps and sender reputation in its own article.

Caching makes all three worse in one way. Once the heavy file is at the edge, every visitor gets it fast but still gets the whole thing. Speed of delivery does not shrink the bytes.

Two readings, two different jobs
What you seeWhat it meansWhere to start
DYNAMIC on the imageCloudflare never cached this fileBypass cookie, page rule, unproxied subdomain, query string
MISS on the imageCacheable but not stored at that momentEdge TTL, recent purge, first visit from that region
HIT and slowCached file is too heavyOriginal size, srcset output, re-encode settings
HIT and fastWorking as intendedLook elsewhere, such as other assets on the page

Why srcset matters more than most settings

A phone screen about 400 pixels wide does not need a 2400-pixel image. It will downscale a huge file after downloading all of it, which wastes the reader's data and time.

Open the page source and search for srcset. If the image tag has no srcset attribute, every visitor downloads one large version. That is a theme or plugin gap, not a caching failure. There is more on checking dns records in a separate guide.

Compare bytes before touching any optimization setting

Cloudflare dashboard DNS records list with grey and orange clouds
Cloudflare dashboard DNS records list with grey and orange clouds

Picture someone who toggles Polish on, purges everything, and reloads. The image is still slow. They never knew what size was being served in the first place, so nothing they changed was measurable.

Get two numbers first. The served size is the content-length value in the response headers, in bytes. The disk size is the file size shown in your hosting file manager or downloaded by FTP.

Divide the served size by the disk size. A ratio near 1 means Cloudflare is passing your file through untouched. A ratio well below 1 means it re-encoded and shrank it. A ratio above 1 means the served file is bigger than your original, which points at the optimizer. It helps to understand blog income strategies before going further.

Write both numbers down before you change a single setting. Without a before, you have no way to know whether a setting helped, and you will keep toggling things at random.

  • Under 100 KB: fine for most blog images, though a large hero can still be slow.
  • 100 to 300 KB: acceptable, but check the display size before accepting it.
  • 300 KB to 1 MB: expect visible delay on mobile connections, even on a HIT.
  • Over 1 MB: resize first, because no cache setting fixes a file this large.

Why purge everything can leave an image DYNAMIC again

Say you purge everything, reload, and the image is quick. Ten minutes later it feels slow again and the header reads DYNAMIC. The purge worked. The cause of the bypass did not go away.

A purge clears stored copies. It does not change a page rule, remove a bypass cookie, or proxy a grey-clouded subdomain. If the file was never cacheable, purging just empties a cache that was already empty for that URL.

Test the fix properly. Use a hard refresh at first, but confirm with curl -I afterwards, because that request carries no browser cache and no login cookie. If the status is HIT in curl, the fix is real.

Check it twice, spaced apart. Most edge caches expire a stored file after a period you set or your host sets. A HIT now and a MISS an hour later usually means the edge TTL is short, not that the fix failed.

  1. Fix the bypass cause first. Remove the bypass rule, proxy the subdomain, or stop appending the query string.
  2. Purge the specific image URL rather than everything. It is faster and easier to trace.
  3. Request the URL with curl -I and read cf-cache-status. Repeat until it returns HIT.
  4. Reload the post in a browser with Ctrl+Shift+R and confirm the image feels quick.
  5. Wait about an hour and check the header again to see whether the HIT held.

The order of work that stops you tuning the wrong thing

Image editor showing a huge original next to a small preview size
Image editor showing a huge original next to a small preview size

Say you turn on Polish, convert to WebP, and resize in one afternoon. The images still feel slow, and you cannot tell which change helped because three things moved at once.

Work in a fixed order and you only measure each change once. Cacheability comes first, because an uncached file is a different problem with a different fix. Re-encoding comes second, since it only matters once the file is being served from the edge. Resizing comes last, because it changes the source file and therefore everything downstream.

Test after each step. A single curl -I before and after takes seconds and tells you whether that step mattered. If the header still reads DYNAMIC, stop and go back, because no image setting will help yet.

If you would rather not manage cache rules, uploads and re-encoding by hand, a host that handles this for you removes the whole class of problem. Hostinger includes an inbuilt CDN along with NVMe storage and a free SSL certificate, so images are served from closer to the reader without you editing headers.

Be clear about what it does not do. It will not resize a 3000-pixel upload for you, enforce srcset on a theme that ignores it, or diagnose a page rule that bypasses caching. You will still need to check upload sizes before publishing.

The free path stays open. Fix the cache bypass with the steps above, resize your largest images to the width they actually display, and check the header after each change. That costs nothing but a little time.

Order of work and what to check after each step
StepWhat you changeWhat you check next
1. CacheabilityBypass rule, subdomain proxy, query stringcf-cache-status returns HIT
2. Re-encodingImage optimization and format settingscontent-length is lower, not higher
3. Source sizeUpload width, theme srcset outputDisplay size matches served size
The verdict

Read cf-cache-status on the image URL first. It splits a cache bypass from a too-heavy file, and those two need opposite fixes.

Questions people still ask

Why does the header say HIT but the image is still slow?

A HIT means Cloudflare stored and served the file. It says nothing about how heavy that file is. Check content-length against the file on disk, then look at the upload width and whether your theme emits srcset candidates.

Can Cloudflare image optimization make an image bigger?

It can. Re-encoding an already-compressed file, or serving WebP at a very high quality setting, sometimes produces a heavier file than the original. Compare the served bytes against the disk bytes before and after you change any optimization setting.

Why is the image DYNAMIC when I am logged into WordPress?

WordPress sets login cookies, and many Cloudflare setups bypass the cache for any request carrying them. Log out and test the same image URL again. If it becomes HIT, logged-out visitors were never affected.

Does purging everything fix slow images?

Only when the stored copy was stale. A purge does not change a page rule, a bypass cookie, or an unproxied subdomain. Confirm with curl -I afterwards, because a browser refresh can hide whether the fix held.

How do I know the fix actually worked?

Run curl -I on the image URL and check that cf-cache-status returns HIT. Then check again about an hour later. If it holds, the edge is storing your file and the remaining slowness comes from the file size.

The thing I check first on any slow-image report is the header on the image URL itself, because it saves an hour of changing settings that were never the problem.