WordPress WebP/AVIF Images Not Showing: Root Causes and the Correct Setup

Was this helpful?

Everything looks fine in the media library. Your theme hasn’t changed. The page HTML has <img> tags. And yet—blank squares, broken thumbnails, or “image cannot be displayed because it contains errors.”

This is the WebP/AVIF failure pattern: the image exists, the URL resolves, and the browser still refuses to render it. The cause is usually not “WordPress.” It’s a chain of tiny configuration decisions: MIME types, caching keys, content negotiation, conversion tooling, and a plugin that got a little too confident.

Fast diagnosis playbook

If you have five minutes before someone declares a “major incident,” do this in order. The goal is to find the bottleneck quickly: is it the browser refusing the bytes, the server returning the wrong headers, or a CDN caching the wrong variant?

First: confirm what the browser is receiving

  1. Open DevTools → Network → click the failing image request.
  2. Check Status (200/304/403/404/500), Content-Type, Content-Length, and any errors like “CORB,” “MIME type mismatch,” or “Failed to decode image.”
  3. Look at Response body: is it actually an image, or HTML (like a 403 page, login page, or WAF block)?

Second: reproduce with curl from a clean viewpoint

  1. Fetch the URL with headers: confirm Content-Type: image/webp or image/avif, not text/html.
  2. Try with and without Accept headers to catch broken content negotiation.
  3. Compare CDN edge response vs origin response.

Third: check caching keys and variant logic

  1. Does the CDN vary cache on Accept? If not, one unlucky request can poison the cache (WebP served to a non-WebP client, or vice versa).
  2. Is a WordPress plugin rewriting URLs to .webp/.avif without ensuring the files exist?
  3. Is the server sending the wrong Content-Type for .webp/.avif?

Fourth: only then touch WordPress

  1. Inspect the generated HTML: src, srcset, picture/source order.
  2. Check plugins doing conversion, lazy-load, CDN offload, and cache/minify. Disable one at a time.
  3. Verify the files exist where WordPress thinks they are (wp-content/uploads or offload storage).

What actually breaks when WebP/AVIF don’t show

Browsers are forgiving about a lot of things. Image decoding is not one of them. When an image “doesn’t show,” it’s usually one of four categories:

  • Wrong bytes: The URL ends in .webp but returns JPEG bytes (or HTML). Some browsers try, most refuse.
  • Wrong headers: Correct bytes, wrong Content-Type (like text/plain) triggers strict behavior or breaks intermediaries.
  • Wrong variant: CDN caches AVIF response and serves it to a client that only supports WebP/JPEG, because the cache key ignored Accept.
  • Wrong HTML: Your markup points to .avif directly without fallback, or the plugin messed up srcset.

There’s also the “fifth category”: you have a WAF or hotlink protection that blocks requests missing a Referer, and now your optimization plugin is fetching images in a way that trips the rule. Security products love to be helpful like that.

One quote that’s worth keeping on a sticky note, because it applies to every one of these failures: “Hope is not a strategy.”Vernon Sanders, commonly repeated in operations circles (paraphrased idea). You’re not going to guess your way out; you’re going to observe and confirm.

Facts and short history that help you debug

These aren’t trivia for a pub quiz. They explain why your stack behaves the way it does.

  1. WebP shipped from Google in 2010 as a VP8-derived format; early support gaps created years of “works on Chrome” assumptions.
  2. AVIF is based on AV1 (the video codec). Still images inherit AV1’s compression efficiency and complexity, which matters for CPU and conversion time.
  3. Safari’s WebP support arrived late (Safari 14). A lot of legacy WordPress setups still have “if Safari then JPEG” workarounds lying around like old landmines.
  4. AVIF support is newer and more fragmented; some browsers support it, some devices have partial or buggy decoders, and some corporate environments freeze browser versions.
  5. Serving modern formats is often done via content negotiation using the Accept header, which breaks spectacularly if caches don’t vary correctly.
  6. WordPress historically restricted upload MIME types; modern WordPress versions support WebP, but server-side libraries (GD/Imagick) might not.
  7. CDNs can “help” by auto-converting images, but that can conflict with WordPress plugins doing the same job, leading to double-optimization or wrong variants.
  8. AVIF can be smaller than WebP, but slower to encode. Teams often discover this during a deploy window. Bad timing is a format too.
  9. Many broken-image incidents are actually HTML errors served with a 200 status; the browser fetches “an image” and gets a login page.

Joke #1: WebP isn’t “web proof.” If it were, we wouldn’t be here, and I’d be writing about something relaxing—like BGP.

Failure modes: browser, server, CDN, WordPress

1) Browser failure: decode error or unsupported format

If you hardcode .avif into <img src> without a fallback, a non-AVIF browser won’t politely downgrade. It will show nothing. The correct pattern is <picture> with ordered <source type> entries, then a final <img> fallback.

Another browser-side failure: corrupted files. This happens when conversion was interrupted, or when a plugin wrote partial output and still updated metadata.

2) Server failure: wrong MIME types, missing modules, bad rewrites

Serving WebP/AVIF isn’t hard, but it’s easy to get wrong in ways that look right at a glance.

  • MIME type missing: Server returns application/octet-stream or text/plain. Some browsers still render; some intermediaries won’t.
  • Rewrite rules too clever: You rewrite .jpg to .webp unconditionally. If the .webp doesn’t exist, you 404 images site-wide.
  • Static file handler vs PHP: A misconfigured location block sends .webp through PHP (slow) or blocks it via security rules.

3) CDN failure: cache poisoning by variant mismatch

This is the big one. Your origin does the right thing: if Accept includes image/avif, it serves AVIF; else WebP; else JPEG. Then your CDN caches the first response it sees under a URL-only key. The next client gets the cached AVIF even if it doesn’t support AVIF. Boom: broken images, but only for some users, and only sometimes. Perfect for wasting afternoons.

4) WordPress failure: plugins rewriting URLs and metadata

WordPress doesn’t natively do full content negotiation for modern formats. Most sites rely on a plugin or CDN feature. Plugins vary widely in quality. Common breakage points:

  • They generate .webp/.avif but don’t update srcset correctly.
  • They update DB references but fail to regenerate thumbnails.
  • They don’t respect offloaded media (S3-compatible storage) and generate local paths that will never exist.
  • They conflict with cache/minify plugins that rewrite HTML after the fact.

Practical tasks (commands, outputs, decisions)

Below are real checks you can run. Each one includes: a command, the output you care about, and what decision to make next. Run them from a server shell (or your laptop for curl checks). Don’t “change things to see if it helps.” Observe first.

Task 1: Confirm the failing URL returns an actual image (not HTML)

cr0x@server:~$ curl -sS -D - -o /tmp/img.bin "https://example.com/wp-content/uploads/2025/11/hero.avif" | sed -n '1,12p'
HTTP/2 200
date: Sat, 27 Dec 2025 10:12:01 GMT
content-type: image/avif
content-length: 48231
cache-control: public, max-age=31536000
etag: "a1b2c3"

What it means: Status is 200 and content-type matches AVIF. Good sign.

Decision: If content-type is text/html or the file is tiny (like 1–5 KB), open the body: you’re probably getting an error page, WAF block, or redirect chain.

Task 2: Identify the file type by magic bytes (don’t trust extensions)

cr0x@server:~$ file -b /tmp/img.bin
ISO Media, AVIF Image

What it means: The bytes are actually AVIF.

Decision: If it says “HTML document” or “JPEG image data,” you have rewrite/CDN/origin confusion. Fix that before touching WordPress.

Task 3: Check what happens for clients without AVIF/WebP support

cr0x@server:~$ curl -sS -D - -o /dev/null -H "Accept: image/png,image/*;q=0.8,*/*;q=0.5" "https://example.com/wp-content/uploads/2025/11/hero"
HTTP/2 200
content-type: image/jpeg
vary: Accept

What it means: The server negotiates and returns JPEG, and it sets Vary: Accept. That’s what you want.

Decision: If there’s no Vary: Accept, your CDN may cache the wrong variant unless you configure it explicitly. Add Vary and ensure CDN honors it (or use separate URLs per format).

Task 4: Compare CDN edge vs origin response headers

cr0x@server:~$ curl -sS -D - -o /dev/null -H "Accept: image/avif,image/webp,*/*" "https://example.com/wp-content/uploads/2025/11/hero.jpg" | egrep -i 'content-type|vary|cache-control|cf-cache-status|x-cache|age'
content-type: image/avif
vary: Accept
cache-control: public, max-age=31536000
age: 8421
x-cache: HIT

What it means: CDN is serving AVIF from cache (HIT), and Vary is present.

Decision: If the edge response differs from origin (wrong content-type, missing vary), purge the CDN and fix cache key / vary handling. Don’t keep purging as a lifestyle.

Task 5: Validate nginx knows WebP/AVIF MIME types

cr0x@server:~$ nginx -T 2>/dev/null | sed -n '/types {/,/}/p' | egrep -n 'image/webp|image/avif' || echo "missing"
missing

What it means: nginx config doesn’t define WebP/AVIF in its types mapping.

Decision: Add MIME types in the right place (often /etc/nginx/mime.types or an included file), reload nginx, and re-test headers.

Task 6: Confirm Apache is serving correct MIME types

cr0x@server:~$ apachectl -M 2>/dev/null | egrep 'mime|headers'
 mime_module (shared)
 headers_module (shared)

What it means: Apache has the modules you need for AddType and Header rules.

Decision: If mime_module is missing, Apache may serve unknown types incorrectly. Enable it, then define AddType image/webp .webp and AddType image/avif .avif.

Task 7: Check if the server is accidentally gzipping images

cr0x@server:~$ curl -sS -D - -o /dev/null -H "Accept: image/avif" "https://example.com/wp-content/uploads/2025/11/hero.avif" | egrep -i 'content-encoding|content-type'
content-type: image/avif

What it means: No Content-Encoding: gzip (good). Compressing already-compressed formats is pointless and sometimes harmful with buggy intermediaries.

Decision: If you see content-encoding: gzip for images, fix your compression rules to exclude image/* (especially WebP/AVIF).

Task 8: Confirm the file exists on disk (origin sanity check)

cr0x@server:~$ sudo ls -lh /var/www/html/wp-content/uploads/2025/11/hero.avif
-rw-r--r-- 1 www-data www-data 48K Nov 20 09:14 /var/www/html/wp-content/uploads/2025/11/hero.avif

What it means: The origin has the file, permissions look normal.

Decision: If it’s missing, the plugin never generated it, or you offloaded media and forgot that the origin filesystem is not the source of truth anymore.

Task 9: Detect partial/corrupt conversions with ffmpeg or avifdec

cr0x@server:~$ ffmpeg -v error -i /var/www/html/wp-content/uploads/2025/11/hero.avif -f null - 2>&1 | head

What it means: No output indicates ffmpeg could parse and decode it without errors.

Decision: If you see decode errors, regenerate that asset and investigate your converter toolchain (CPU/memory/timeouts).

Task 10: Verify WordPress thinks the attachment metadata is sane

cr0x@server:~$ wp post meta get 123 _wp_attachment_metadata --format=json | head -c 220; echo
{"width":2400,"height":1600,"file":"2025/11/hero.jpg","sizes":{"thumbnail":{"file":"hero-150x150.jpg","width":150,"height":150,"mime-type":"image/jpeg"}...

What it means: WordPress metadata references the original JPEG and its derived sizes. Many plugins store WebP/AVIF separately.

Decision: If metadata points to .webp/.avif but the files don’t exist (or vice versa), you have a mismatch. Regenerate thumbnails and re-run the plugin’s “bulk optimize” with correct settings.

Task 11: Inspect rendered HTML for picture, srcset, and order

cr0x@server:~$ curl -sS "https://example.com/" | sed -n '1,220p' | egrep -n '
119:  
120:  
121:  
122:

What it means: This is correct: AVIF first, WebP second, JPEG fallback last.

Decision: If you see <img src="...avif"> with no fallback, fix the theme/plugin. If type is wrong or missing, some browsers won’t select the source.

Task 12: Catch “blocked by WAF/hotlink protection” pretending to be images

cr0x@server:~$ curl -sS -D - -o - "https://example.com/wp-content/uploads/2025/11/hero.webp" | sed -n '1,25p'
HTTP/2 403
content-type: text/html; charset=UTF-8
server: nginx
...
<html><head><title>Access denied</title></head>...

What it means: 403 with HTML body. Browser requested an image, got a denial page.

Decision: Adjust WAF rules, hotlink protection, or signed URL logic so legitimate image requests are allowed. Also ensure your image optimizer is not fetching images with odd headers that trigger blocks.

Task 13: Verify CDN/origin returns correct Content-Length and supports range requests

cr0x@server:~$ curl -sS -D - -o /dev/null -H "Range: bytes=0-1023" "https://example.com/wp-content/uploads/2025/11/hero.webp" | egrep -i 'http/|content-type|content-range|accept-ranges'
HTTP/2 206
content-type: image/webp
accept-ranges: bytes
content-range: bytes 0-1023/61244

What it means: Range requests work. This matters for some clients, intermediaries, and performance behavior.

Decision: If ranges fail and your CDN expects them, you can get partial loads or strange client behavior. Fix server static file handling and proxy settings.

Task 14: Confirm image conversion support in PHP libraries (GD/Imagick)

cr0x@server:~$ php -r 'echo "GD: ".(function_exists("gd_info")?gd_info()["GD Version"]:"missing").PHP_EOL;'
GD: bundled (2.1.0 compatible)
cr0x@server:~$ php -r 'print_r(function_exists("gd_info")?gd_info():["gd"=>"missing"]);' | egrep -i 'WebP Support|AVIF Support|JPEG Support'
WebP Support => 1
AVIF Support => 0
JPEG Support => 1

What it means: GD can write WebP but not AVIF. Many WordPress plugins fall back silently or generate only some formats.

Decision: If you need AVIF, use a converter that supports it (often via Imagick with libheif, or external tools), or delegate format conversion to the CDN and keep origin as JPEG/PNG/WebP.

Task 15: Check Imagick codec support

cr0x@server:~$ php -r 'echo extension_loaded("imagick")?"imagick loaded\n":"imagick missing\n";'
imagick loaded
cr0x@server:~$ php -r '$i=new Imagick(); print_r($i->queryFormats());' | egrep -E 'WEBP|AVIF|HEIC' | head
AVIF
WEBP

What it means: Imagick supports AVIF and WebP on this host. That’s rare enough to celebrate quietly.

Decision: If AVIF is missing, you can install/build Imagick with libheif support, or stop promising AVIF from the origin and use WebP + JPEG fallback.

Task 16: Check for rewrite rules that force .webp even when missing

cr0x@server:~$ sudo nginx -T 2>/dev/null | egrep -n 'try_files|\.webp|Accept'
214:    if ($http_accept ~* "webp") {
215:        rewrite ^(.+)\.(jpe?g|png)$ $1.webp last;
216:    }

What it means: This rewrite is unconditional. If hero.webp doesn’t exist, you’ll 404.

Decision: Replace rewrites with try_files logic that checks existence and falls back safely.

Correct setup: sane, boring, reliable

You want modern formats without modern-format drama. Here’s the setup that survives traffic, CDNs, and plugin upgrades.

Principle 1: Prefer <picture> over URL rewriting

If you can control markup generation (theme level or a well-behaved plugin), use:

  • <source type="image/avif"> first
  • <source type="image/webp"> second
  • <img src="...jpg/png"> as fallback

This avoids cache-key complexity because different formats can have different URLs. It’s not as “clever,” which is the point.

Principle 2: If you do content negotiation, treat caching as a first-class feature

Content negotiation means same URL, different bytes. That only works when:

  • Origin sends Vary: Accept
  • CDN caches separately per Accept (or you configure separate cache keys)
  • You have a purge strategy when you change conversion settings

Without these, negotiation becomes “random image lottery.”

Principle 3: Don’t require AVIF if you can’t reliably produce it

AVIF is great. It’s also a bit of a diva in server environments: you need the right libraries, and conversion is CPU-expensive. If you can’t guarantee conversion completion and correct headers, use WebP first and treat AVIF as optional.

Server configuration: nginx (minimal, correct)

Make sure nginx knows the MIME types. In your mime.types or http block:

cr0x@server:~$ sudo sed -n '1,120p' /etc/nginx/mime.types | tail -n 12
    image/webp                           webp;
    image/avif                           avif;

If you must map JPEG/PNG to WebP when available, do it with try_files, not unconditional rewrite. Example pattern (conceptual; adapt carefully to your paths):

cr0x@server:~$ sudo nginx -T 2>/dev/null | sed -n '180,260p'
location ~* \.(png|jpe?g)$ {
    add_header Vary Accept;
    set $webp_suffix "";
    if ($http_accept ~* "image/webp") {
        set $webp_suffix ".webp";
    }
    try_files $uri$webp_suffix $uri =404;
}

Why this works: It only serves .webp if it exists. Otherwise it serves the original. No 404 storms.

Server configuration: Apache (minimal, correct)

Define MIME types and make sure headers module is available. In an Apache config or .htaccess (if allowed):

cr0x@server:~$ sudo apachectl -t -D DUMP_INCLUDES 2>/dev/null | head
Included configuration files:
  (/etc/apache2/apache2.conf)
  (/etc/apache2/conf-enabled/*.conf)
  (/etc/apache2/sites-enabled/000-default.conf)

Then add types (where appropriate):

cr0x@server:~$ sudo sed -n '1,60p' /etc/apache2/mods-enabled/mime.conf | tail -n 10
AddType image/webp .webp
AddType image/avif .avif

WordPress configuration: decide who owns conversion

You have three viable ownership models. Pick one and commit. Mixing them creates ghost bugs.

  1. Plugin owns conversion, origin serves static modern formats: Most common. Works if your server has the libraries and you manage cache invalidation.
  2. CDN owns conversion: Origin stores JPEG/PNG, CDN serves WebP/AVIF to capable clients. Less load on your servers, fewer library headaches. Requires CDN configuration discipline.
  3. Build pipeline owns conversion: You convert images before upload/deploy. Great for brand sites and controlled media workflows, less great for user-generated content.

Pick a fallback strategy and test it like production depends on it (it does)

Minimum acceptable behavior:

  • Any client gets an image (JPEG/PNG at worst).
  • No plugin should replace the only URL with an AVIF URL without fallback.
  • Broken conversion must not break existing pages (serve old format while new is processing).

Joke #2: The only “lossless” thing in image optimization is your patience when the cache key ignores Accept.

Common mistakes (symptoms → root cause → fix)

1) Symptom: Broken images only on Safari or older devices

Root cause: You’re serving AVIF (or WebP) without fallback, or the CDN cached AVIF and serves it to everyone.

Fix: Use <picture> with JPEG fallback, or ensure Vary: Accept and CDN cache key vary on Accept. Purge after changes.

2) Symptom: DevTools shows 200 OK but image is blank

Root cause: Response body is not an image (HTML denial page, WAF block, or a “soft 404” page) served with 200 or 403.

Fix: Fetch with curl and inspect Content-Type and magic bytes. Fix WAF/hotlink rules; ensure static paths bypass auth and security challenges.

3) Symptom: Some users see images, some see broken icons; flipping caches “fixes” it temporarily

Root cause: CDN cache poisoning due to missing vary/correct cache key. First request wins.

Fix: Configure CDN to vary on Accept (or use separate URLs per format). Ensure origin sets Vary: Accept. Purge affected paths.

4) Symptom: 404 for .webp URLs after enabling an optimization plugin

Root cause: Plugin rewrote image URLs to .webp but didn’t generate those files (or failed mid-batch).

Fix: Disable URL rewriting, run bulk generation, confirm filesystem/offload targets, then re-enable. Add safe server-side try_files fallback if you insist on rewrite behavior.

5) Symptom: Images download instead of display

Root cause: Wrong Content-Type, sometimes application/octet-stream.

Fix: Add correct MIME types in nginx/Apache. Re-test headers. Some CDNs also need explicit type mapping.

6) Symptom: Thumbnails are broken but original images load

Root cause: Derived sizes weren’t converted/regenerated; srcset points to variants that don’t exist.

Fix: Regenerate thumbnails; rerun converter for all sizes; verify metadata for a sample attachment.

7) Symptom: High CPU spikes when enabling AVIF; pages time out

Root cause: Conversion runs on-demand during requests, or batch jobs starve the host. AVIF encoding is expensive.

Fix: Do offline/batch conversion with limits; use queue workers; or let CDN do conversion. Put hard caps on concurrent encodes.

8) Symptom: Only the admin sees correct images; logged-out users don’t

Root cause: Cache varies by cookies/auth, or optimization plugin bypasses conversion for logged-in users, or CDN serves different variants.

Fix: Compare responses with/without cookies. Fix cache rules and ensure the same URL resolves to correct asset for anonymous traffic.

Checklists / step-by-step plan

Step-by-step: get to a correct baseline (no heroics)

  1. Pick ownership: plugin, CDN, or build pipeline. Do not run two converters.
  2. Establish fallback HTML: prefer <picture> with JPEG/PNG fallback.
  3. Fix MIME types: ensure image/webp and image/avif are correct at origin and CDN.
  4. Confirm bytes match headers: file against downloaded content; no HTML error pages.
  5. Fix cache variation: if negotiating by Accept, you must use Vary: Accept and CDN cache key variation.
  6. Regenerate derivatives: thumbnails and responsive sizes are where silent breakage hides.
  7. Load test conversion workflow: AVIF encodes can saturate CPU. Plan where the heat goes.
  8. Set up monitoring: track 404/403 rates on .webp/.avif, track response Content-Type mismatches if you can.

Release checklist for enabling AVIF/WebP in production

  • At least three browser profiles tested: AVIF-capable, WebP-only, and neither (forced Accept header test works).
  • CDN cache key verified for variant behavior, or separate URLs per format used.
  • Origin returns correct MIME types and does not gzip images.
  • Conversion jobs are off-request-path or strictly limited.
  • Rollback plan: disable rewriting/negotiation without breaking image URLs.

When something is broken: containment checklist

  • Stop the bleeding: disable URL rewriting that forces .avif/.webp if files aren’t guaranteed.
  • Purge CDN caches for affected paths only (don’t carpet-bomb unless you enjoy self-inflicted outages).
  • Revert to JPEG/PNG fallback markup if your theme/plugin supports it.
  • Confirm 200 responses are actual images (not HTML).

Three corporate mini-stories from the trenches

Mini-story 1: The incident caused by a wrong assumption

A mid-sized commerce site wanted “modern images everywhere.” The plan sounded clean: enable AVIF conversion in the optimization plugin, flip on nginx rewrites so .jpg becomes .avif for supporting browsers, then let the CDN do its usual thing.

Someone assumed the CDN automatically varied cache by Accept. It did not, at least not for that cache rule. The first request after deploy came from a Chrome client that advertised AVIF support. The CDN cached the AVIF response under the JPEG URL.

The next wave of traffic included embedded browsers in mobile apps and older devices. They requested the same JPEG URL, got AVIF bytes, and failed to decode. The incident dashboard showed “image load failures” but origin health was green. Because of course it was.

The fix was not magical: configure cache variation correctly (or stop negotiating on the same URL), purge the poisoned objects, and add a test that curls a representative set of image URLs with different Accept headers before every rollout. The lesson wasn’t “don’t use AVIF.” It was “don’t outsource your assumptions to a cache.”

Mini-story 2: The optimization that backfired

An internal comms platform had grown large enough that media storage was a real budget line. Someone proposed aggressive AVIF for everything, with on-demand conversion: when a user first views a page, the server converts missing AVIF variants and caches them. It would “spread the work out.”

It did spread the work out. Unfortunately, it spread it onto the web tier during peak traffic. The conversion library was CPU hungry. Requests queued. The PHP workers got pinned waiting on conversions and filesystem writes. Latency went up, then timeouts, then the cache layer started retrying upstream, which is never a love story.

Worse, a few conversions were interrupted. Partial files were written, but the metadata update succeeded. Some users got corrupt AVIFs served with a correct Content-Type. Those are the most annoying failures because they look “right” until you actually decode the bytes.

The rollback was simple: stop on-demand conversion, move conversion to a controlled queue with concurrency limits, and serve WebP/JPEG until variants are ready. Disk savings returned, but now the system stopped trying to set itself on fire to save a few kilobytes.

Mini-story 3: The boring practice that saved the day

A publishing company ran multiple WordPress properties behind the same CDN. They had a rule that sounded painfully unsexy: every new asset type gets a “header contract test” in CI. It checks status codes, Content-Type, Cache-Control, and variant behavior with different Accept headers.

When they added WebP and later AVIF, they didn’t just click the plugin toggle and pray. They deployed to a staging domain that used the same CDN configuration as production. The CI job fetched a set of canonical images through the CDN and directly from origin, compared headers, and failed the build if they diverged.

On the first AVIF trial, the test failed: the CDN stripped Vary: Accept on one path-based cache rule. Nobody noticed in manual testing because their browsers were all modern. The test did notice, because it forced an Accept header that excluded AVIF and expected a JPEG fallback.

They fixed the CDN rule before users ever saw it. Nothing exploded. No one got paged. That’s what “boring” buys you: sleep and credibility.

FAQ

1) Should I serve AVIF, WebP, or both?

Both, if you can do it reliably. AVIF first for supported clients, WebP second, JPEG/PNG fallback last. If your server can’t encode AVIF reliably, serve WebP + JPEG and keep AVIF optional (CDN-side is a common compromise).

2) Why do I get 200 OK but the image still doesn’t display?

Because “200” is not a promise that the bytes are an image. Check Content-Type and inspect the body. A 200 login page served to an image URL will still be a broken image.

3) Do I need to add MIME types for WebP and AVIF?

Yes. Many servers won’t guess correctly. You want image/webp and image/avif. Wrong MIME types can cause downloads, blocked rendering, or weird CDN behavior.

4) Can I rely on a WordPress plugin alone?

You can, but verify the entire pipeline: conversion tool support (GD/Imagick), thumbnail regeneration, and how the plugin rewrites HTML/URLs. Plugins often work perfectly until you add a CDN, offload media, or another “helpful” optimization plugin.

5) What’s the safest way to implement modern formats on WordPress?

Use <picture> markup and distinct URLs per format. It avoids cache variation complexity. If you need negotiation, treat Vary: Accept and cache keys as non-negotiable requirements.

6) My CDN says it supports WebP/AVIF automatically. Why are images broken?

Because “supports” can mean “can convert,” not “will vary cache correctly for your rules.” Confirm cache key behavior, confirm Vary: Accept behavior, and check whether your origin already does conversion—double conversion and variant confusion are common.

7) Why do only thumbnails break?

Because thumbnails are separate files. Your original might have been converted, but the derived sizes used by srcset were not generated or were generated in a different location (especially with offloaded media).

8) Is AVIF always smaller than WebP?

Often, but not always. Content matters. AVIF can be great for photographic images, but it can be slower to encode and sometimes not worth it for small UI assets. Measure; don’t assume.

9) Why did enabling AVIF spike CPU and slow the site?

Because encoding is expensive. If conversions happen on request, you’ve put a video-codec-grade workload on your web tier. Move conversion to background jobs, limit concurrency, or offload conversion to your CDN.

10) Do I need to purge caches after enabling WebP/AVIF?

Usually, yes—especially if you changed URL rewriting, negotiation rules, or CDN behavior. Otherwise you may serve stale variants, or keep a poisoned cache object alive far longer than your patience.

Next steps you can do today

If WebP/AVIF images aren’t showing, don’t start by reinstalling plugins. Start by confirming what bytes are being served, with what headers, and who cached them.

  1. Pick one broken image URL and run the curl + file checks. Confirm: status, Content-Type, and magic bytes all agree.
  2. If you use content negotiation, verify Vary: Accept and CDN cache-key behavior. If you can’t guarantee that, switch to <picture> with separate URLs per format.
  3. Confirm server MIME types for .webp and .avif. Fix them at the origin; don’t rely on “the CDN will figure it out.”
  4. Audit your WordPress plugins: one converter, one HTML rewriter, one cache plugin (or at least, one that’s in charge). Everything else is just a contest of who breaks markup last.
  5. Regenerate thumbnails and validate srcset for a few representative attachments.

Modern formats are worth it. Just don’t implement them like a magic trick. Do it like an operator: observable behavior, safe fallbacks, and caches that are configured—not hoped for.

← Previous
Intel Arc: why a third player matters even if you don’t buy it
Next →
Windows Vista: the release that taught people to fear updates

Leave a comment