Image optimization for SEO: format, WebP, lazy load, srcset

Open the network panel on almost any article page and sort by size. The heaviest rows are rarely the scripts or the stylesheet. They are the images. Text arrives in kilobytes; a single unoptimized hero can arrive in megabytes, and on a typical content page the images together outweigh the HTML, CSS, and JavaScript combined by a wide margin.

That weight is also the most fixable performance problem a site has, because the same handful of moves that shrink an image improve three things at once: page speed, the experience of visitors on slow connections, and search performance, since Google’s page-experience signals reward faster loads. This guide covers the four levers that move image weight the most, plus how those levers surface in Core Web Vitals. It is about the binary file itself, not the text describing it. The alt-text side of image SEO is its own subject and is not covered here.

Format choice: the largest single lever #

The format decides how the file is encoded, which directly controls file size for the same visual content. Five formats matter in 2026.

Format Best at Worst at
JPG (JPEG) Photographs, images with many colors Sharp edges, text, transparency
PNG Graphics, screenshots, transparency Photographs (much larger than JPG)
WebP Photos and graphics, smaller than JPG or PNG at equal quality Very old browsers (needs a fallback)
AVIF Smallest files for both photos and graphics Support is high but not universal
SVG Vector logos, icons, illustrations Photographic content

The decision tree is short. For photographs and complex images, WebP is the default. Google’s official WebP compression study reports files 25 to 34 percent smaller than JPG at equivalent visual quality (measured by the SSIM index), and every major browser now supports it. JPG stays as the universal fallback for the rare case where WebP is not available. AVIF produces smaller files still, roughly half the size of an equivalent JPG and around 20 percent smaller than WebP for typical photos, with browser support now near-universal but not quite total, which is why a fallback still matters. For graphics, screenshots, and anything with transparency or hard edges, PNG or WebP-lossless are the choices. For logos and icons that scale to many sizes, SVG renders crisply at any size and its markup is text that compresses well.

Serving a modern format with a fallback is the job of the <picture> element:

<picture>
  <source srcset="image.avif" type="image/avif">
  <source srcset="image.webp" type="image/webp">
  <img src="image.jpg" alt="Description">
</picture>

The browser walks the list top to bottom, takes the first format it supports, and ignores the rest. Modern browsers get AVIF or WebP; older ones fall back to JPG. One block of HTML serves every device correctly.

Lossy vs lossless: how much quality to keep #

Compression is the second lever, and the choice depends on what the image contains. Lossy compression discards visual information to shrink the file. The discarded data is calibrated to be invisible at normal viewing distance, but pushed too far it produces visible artifacts: blocky edges, color banding, blurred detail. JPEG, WebP, and AVIF all support it, on a quality scale from 0 (maximum compression, severe artifacts) to 100 (minimal compression, larger file). Lossless compression keeps every pixel and shrinks the file through encoding efficiency alone. PNG is lossless by default; WebP does both. The trade is that lossless files run larger than lossy ones, sometimes several times larger for the same content.

A working calibration:

Image type Recommended quality Approach
Hero images, photography 80 to 85 percent (JPG/WebP) Lossy, favor visual quality
Inline article photos 75 to 80 percent (JPG/WebP) Lossy, balance quality and size
Thumbnails, icons 70 to 75 percent (JPG/WebP) Lossy, favor size
Logos with sharp edges Lossless (PNG/WebP) Preserve every pixel
Screenshots with text Lossless or 90 percent-plus lossy Text legibility matters

Tools automate this. Squoosh and ImageOptim produce output comparable to manual tuning, and CMS plugins such as ShortPixel, Imagify, and Smush compress on upload against a quality preset. The final check is always visual: compress at a few quality levels, look at the result at the size it actually displays on the page, and pick the lowest quality that still looks right. Most images tolerate more aggressive compression than intuition suggests. Hero and product photography usually need higher settings; decorative and supporting images can drop further with no one noticing.

Lazy load: load what is visible, defer what is not #

A typical article holds a dozen or more images, but the reader sees only the first one or two before scrolling. Loading all of them upfront spends bandwidth on images the reader may never reach and slows the render of what is actually on screen. Lazy loading defers each download until the image is about to enter the viewport:

<img src="image.jpg" loading="lazy" alt="Description">

Native lazy loading shipped in Chrome in 2019, reached Firefox in 2020, and arrived in Safari in early 2022, so by 2026 it covers nearly all browsers in active use, per caniuse.com. The browser handles the deferral itself, fetching each image just before it scrolls into view. The rules are simple: add loading="lazy" to below-the-fold images, and do not add it to anything visible on initial load. The explicit opposite value is loading="eager", which is also the default when no attribute is set.

Lazy-loading the hero is a real and common mistake. The above-the-fold image is usually the Largest Contentful Paint element, and deferring it delays that metric and can flash unloaded content. Keep the hero eager. For that image specifically, a companion attribute helps more than eager loading alone:

<img src="hero.webp" fetchpriority="high" alt="Hero image">

Browsers assign images a low default priority during parsing because they cannot yet tell which one matters most. Setting fetchpriority="high" overrides that and pulls the image forward in the fetch queue. Support reached the major engines (Chrome 102 and later, Firefox 132 and later, Safari 17.2 and later) and covers most users, and because it is a hint, older browsers simply ignore it with nothing broken. Use it on the single most likely LCP image only; marking several high defeats the purpose, since they then compete with each other. How the hero fetch fits into the full LCP picture, including preloading, is a separate diagnostic and is not covered in depth here.

The same loading="lazy" attribute works on iframes. An article with three embedded YouTube videos and four images can defer most of its third-party content the same way. For sites still supporting genuinely old browsers, JavaScript libraries such as lozad.js cover the legacy case, but native support handles nearly everything now, so most sites no longer need both.

Responsive images: the right image for the right screen #

A high-resolution image that looks crisp on a 4K monitor is overkill on a phone that needs maybe 600 pixels wide. Sending the desktop file to the phone wastes bandwidth and slows the page. The srcset attribute lets the browser pick the right version:

<img src="image-800w.jpg"
     srcset="image-400w.jpg 400w,
             image-800w.jpg 800w,
             image-1200w.jpg 1200w,
             image-1600w.jpg 1600w"
     sizes="(max-width: 600px) 100vw, 50vw"
     alt="Description">

The browser is handed a list of versions at different widths plus a sizes attribute describing how wide the image will display at various screen sizes, then combines that with the device’s pixel density to fetch the version it actually needs. Three widths cover most cases: a small one for phones (400 to 600px), a medium one for tablets and standard desktops (800 to 1200px), and a large one for high-density displays (1600 to 2000px). A build pipeline or CMS generates all three from one source.

When the crop or composition should change by viewport, not just the resolution, the <picture> element handles that art direction explicitly:

<picture>
  <source media="(max-width: 600px)" srcset="image-mobile.jpg">
  <source media="(max-width: 1200px)" srcset="image-tablet.jpg">
  <img src="image-desktop.jpg" alt="Description">
</picture>

Same scene, different framing per screen, from one block of HTML. Most modern platforms do the mechanical part automatically. WordPress has generated multiple sizes for every upload for years, and its automatic srcset markup landed in WordPress 4.4 in late 2015, which is what made those existing sizes get served responsively without theme work. Shopify, Wix, and similar platforms handle it through their image CDNs. The remaining job for operators is to confirm the responsive variants are actually referenced in templates rather than the full-size original being served in their place.

The CDN does the work the server cannot #

An image CDN generates responsive variants, converts formats, and distributes globally from one master upload. Cloudflare Images, Cloudinary, Imgix, and Bunny.net all work the same way: upload once, and the CDN serves whatever variant the requesting browser needs. The transformation is usually encoded in the URL. A request for cdn.example.com/photo.jpg?w=800&format=webp&q=80 returns an 800-pixel-wide WebP at 80 percent quality; the CDN builds that version on first request, caches it at edge locations, and serves later requests in milliseconds.

This replaces several separate tools at once. Generating responsive variants becomes a URL parameter, format conversion to WebP or AVIF becomes a content-negotiation step, and compression becomes a configurable value, so work that once needed a build pipeline now happens at request time. The trade is cost and dependency: CDNs bill by bandwidth, by transformations, or both, and a high-traffic image site can add real operational expense while taking on reliance on a third-party service. For sites where image performance outweighs that cost, an image CDN is often the single highest-leverage change available, because format conversion, variant generation, and edge caching together cut image weight well below what serving originals from origin achieves.

Where image work shows up in Core Web Vitals #

Image performance feeds directly into the page-experience metrics Google uses, and two of the Core Web Vitals in particular. The full metric system is its own subject; the point here is that image work is Core Web Vitals work whether the operator frames it that way or not.

Largest Contentful Paint, which tracks how long the largest visible element takes to render, is most often an image, and a slow hero is the most common cause of a poor score. The levers above are the same ones that move it: a WebP hero at typical settings (around 800px wide, 80 percent quality) often weighs roughly half its JPG equivalent and downloads faster, and responsive sizing cuts the mobile version smaller still, where the metric matters most. Keeping the hero eager rather than lazy-loaded, and pulling it forward with fetchpriority="high", closes the rest of the gap. The deeper LCP diagnosis, including subpart breakdown and preloading, belongs to its own discussion.

Cumulative Layout Shift, which tracks how much content jumps as the page loads, also depends on image handling. An image without explicit dimensions gives the browser no way to reserve space, so everything below it shifts when the image finally paints. Setting them prevents that:

<img src="image.jpg" width="800" height="600" alt="Description">

The browser reads the width and height, derives the aspect ratio, and reserves the correct space before the file arrives, so the content below stays put and layout shift from that image drops to near zero. This holds even when CSS scales the image responsively, because the ratio, not the fixed pixels, drives the reserved height. The mechanics of CLS as a metric are covered separately; the takeaway here is that dimensions on every image are cheap insurance.

The recurring anti-patterns #

Diagnosing image weight on a slow page usually means working through the same short list of suspects.

  • Serving full-resolution originals. A 4000 by 3000 pixel, 4 MB camera file dropped into an 800-pixel slot. Generate a version sized for the display context; most CMS platforms do this once configured.
  • JPG for graphics. A logo or icon saved as JPG develops artifacts around sharp edges. Use PNG or WebP-lossless for anything with text or hard lines; JPG is for photographs.
  • No lazy loading below the fold. Every image fetches upfront, including ones the reader never reaches. Add loading="lazy" outside the first viewport.
  • Missing width and height. Images load without reserved space and content jumps. Add explicit dimensions to every image, even when it scales responsively.
  • A modern format with no fallback. Older browsers get a broken image. Use <picture> with type-specific sources, or a CDN that handles content negotiation.
  • Lazy-loading the hero. The above-the-fold image is deferred, delaying LCP. Keep it eager, or omit the attribute.
  • Camera filenames. IMG_4729.jpg tells Google nothing; nike-air-zoom-pegasus-40-black.jpg describes the content. Rename descriptively before upload, or have the CMS rewrite filenames from the slug or alt text. Lowercase, hyphens between words, no spaces or special characters, mirroring URL-slug conventions. The filename joins alt text and page context as one of the signals Google uses to understand an image for image search.
  • One image reused at many sizes. A 2000-pixel hero pressed into service as a 200-pixel thumbnail with no smaller variant. Generate distinct versions per context, or use a CDN that resizes on request.

For the rare case that needs machine-readable image context beyond filename and alt text, the ImageObject schema can declare a creator, license, or credit line, which helps sites that attribute photographers or surface in image-specific results. For most sites, descriptive filenames and good alt text cover the essentials; implementing that schema is a separate exercise.

FAQ #

Should I switch everything to AVIF?
AVIF gives the smallest files and support is now near-universal, but not total. Serve it through a <picture> element or a CDN so the small share of unsupported browsers still receives WebP or JPG. There is no need to abandon WebP to adopt AVIF; they coexist in the same fallback chain.

Does WebP hurt SEO because it is a newer format?
No. Google supports and indexes WebP, and the faster load it produces helps the page-experience side of ranking. The only real risk is serving it without a fallback to the few browsers that cannot render it.

Is lazy loading always a win?
Below the fold, yes. Applied to above-the-fold images, especially the hero, it backfires by delaying render and the LCP metric. Lazy-load what the reader has to scroll to, and load the rest eagerly.

Do I still need width and height if I use CSS for sizing?
Yes. CSS controls the displayed size, but the width and height attributes give the browser the aspect ratio it needs to reserve space before the image loads. Omitting them reintroduces layout shift even when CSS handles the final dimensions.

My CMS already generates image sizes. Is that enough?
Often, but verify it. Platforms like WordPress and Shopify create variants automatically, yet templates sometimes reference the full-size original instead. Check what the page actually serves in the network panel rather than assuming the variants are in use.

The levers are not universal in the right combination. A photography portfolio favors quality and accepts larger files; an e-commerce catalog with thousands of product shots favors size and accepts firmer compression; a mostly-text blog optimizes the hero carefully and leaves defaults for the rest. What stays constant is that every lever exists because images carry weight the page would rather not pay. Ignore them all and image weight becomes the dominant performance problem; pull on each one and the images load fast across devices and connections with no one noticing the work behind it. The optimization is invisible when it succeeds, and the absence of it is what gets felt.