Serving WebP Safely: The picture Element and Fallback Strategy
WebP has had universal browser support since Safari 14 shipped in September 2020. That fact leads a lot of teams to drop fallbacks entirely — and mostly they get away with it, right up until an email client, a link-preview scraper or a customer's ancient corporate browser produces a broken image icon in front of someone who matters.
The fallback chain is cheap. Here is how to build one that earns its keep.
The basic pattern
The <picture> element lets you offer several encodings of the same image and have the browser pick the first one it understands:
<picture>
<source srcset="hero.avif" type="image/avif">
<source srcset="hero.webp" type="image/webp">
<img src="hero.jpg" alt="..." width="1200" height="630">
</picture>
Order matters and is evaluated top to bottom. AVIF first because it is smallest, WebP second because it is nearly as small and more widely supported, and the plain <img> last as the universal floor. A browser that understands none of the sources falls back to the src, which is why that should always be a JPG or PNG.
Where WebP genuinely still fails
Email. This is the big one. Outlook renders email through Word's HTML engine, and support for WebP in email clients generally is patchy enough that you should simply never send one. Marketing emails, transactional receipts, password reset templates — use JPG or PNG.
Link-preview scrapers. Open Graph images get fetched by dozens of different services, some of which have not been updated in years. Serve og:image as JPG or PNG. The bandwidth saving on a preview card is not worth a blank thumbnail in a Slack unfurl.
Anything a user will download. If the image is a deliverable — a proof, an invoice attachment, an asset pack — hand over JPG or PNG. The recipient's software is not your problem to solve, and they will not thank you for making it one.
Print pipelines. WebP is sRGB, 8-bit, and has no concept of CMYK or ICC-managed colour separation. No print shop accepts it. Keep a TIFF or PDF master for anything print-bound.
Do not use file extensions to detect support
A pattern that shows up in a lot of hand-rolled code: check the User-Agent string, decide whether the browser supports WebP, and serve accordingly. This is fragile and unnecessary. Browsers announce their support in the Accept header on every image request, and <picture> handles the negotiation declaratively without any server logic at all.
Combining fallbacks with responsive sizes
The two concerns compose. Each <source> takes a full srcset and sizes, so you can offer three formats at four widths each without any special casing:
<source
type="image/webp"
sizes="(max-width: 700px) 100vw, 700px"
srcset="hero-400.webp 400w, hero-800.webp 800w, hero-1600.webp 1600w">
The combinatorics look alarming written out, but this is exactly what build tooling is for. Next.js, Astro, Eleventy's image plugin and every major CDN generate the whole matrix from one source file.
Keep one master, generate everything else
The rule that keeps this manageable over years: store one high-quality master per image — a large PNG, TIFF or full-quality JPG — and treat every WebP, AVIF and resized variant as a build artefact you can delete and regenerate at any time. The moment a WebP becomes the only copy of something, you have lost the ability to change your format strategy later.