Image Optimization for the Web: The Checklist, Minus the Promises
Images are usually the largest single category of bytes on a marketing page. Usually, not always, and you should not take our word for it. Open DevTools, go to the Network tab, filter by Img, and read the transferred total for your own site. That number is your budget. Everything below is how to spend it well.
This is the checklist we run on client sites. It is ordered by how often it changes the number, not by how clever it is. We have removed several claims that earlier versions of this post made without evidence, and we flag those as we go.
1. Serve a modern format, and know which one you are actually serving
WebP and AVIF are both broadly supported. AVIF usually produces smaller files than WebP at a similar perceptual quality, and it is slower to encode.
An earlier version of this post printed a size table (PNG 1.4 MB, JPEG 380 KB, WebP 190 KB, AVIF 110 KB) and called them all "the same visual quality." We are removing that. Those numbers came from one image, and "same visual quality" was an assertion, not a test. Compression quality is content dependent. A photograph with soft gradients and a screenshot with hard text edges fall apart at completely different quality settings. Do not trust a quality number you read on a blog, including this one. Encode your actual hero at two or three settings, look at them side by side at the size they will be displayed, and pick.
If you are on Next.js, know your default. The image component optimizes to WebP out of the box. AVIF is not on by default. You have to enable it in next.config by adding image/avif to the formats array, and you should measure build and request time after you do, because AVIF encoding is expensive.
On plain HTML, use the picture element with source elements for avif and webp and a jpeg or png fallback in the img tag. Browsers take the first format they support.
2. Set the sizes attribute
This is the single most common cause of oversized image downloads on phones. Without sizes, the browser has to guess how wide the image will display, and it guesses generously. A phone rendering the image at 375 CSS pixels can end up downloading the 1920 wide candidate from your srcset.
The sizes attribute is a promise you make to the browser about layout width at each breakpoint. Something like: 100vw up to 768px, 50vw up to 1200px, otherwise 1200px. The browser then picks the smallest srcset candidate that still covers the device pixel ratio. If your sizes value lies about the layout, the browser will pick the wrong file, so check it against the real CSS.
3. Set width and height on every image
Layout shift from images is caused by the browser not knowing the aspect ratio before the file arrives. Setting the width and height attributes (the HTML attributes, not CSS) lets the browser reserve the space immediately. The Next.js image component requires them. On plain HTML you have to audit every img tag.
This is the cheapest fix on the list and it is almost always worth doing.
4. Lazy load below the fold, load the hero eagerly
Native lazy loading needs no JavaScript. Add loading lazy to images below the fold. Leave the LCP image out of it.
For the hero, do the opposite: loading eager plus fetchPriority high. In Next.js 16 the priority prop is deprecated. It has been replaced by explicit controls, because the old prop bundled several behaviors together. Use loading eager and fetchPriority high for a hero that already exists in the server-rendered HTML. Use the preload option only when the browser cannot discover the resource early on its own, for example when the URL is computed in client JavaScript.
A warning that most image posts leave out: preloading is not free. A preload tells the browser to jump the queue, and the queue has a fixed width. Preload three fonts, a video poster, and a background texture and you have taken bandwidth away from the image that is actually your LCP element. Budget roughly one preload, for the LCP resource, and verify in a network waterfall that it arrived earlier and nothing else arrived later.
5. Generate a real srcset
Ship several widths of the same image and let the browser choose. A set of 640, 1024, and 1920 covers most of the range without sending a 4K file to a phone. Next.js generates these for you. On other stacks, Sharp, cwebp, or a hosted pipeline will do batch resizing. Build it into the pipeline so no one has to remember.
6. Write alt text for the person who cannot see the image
Alt text exists so that screen reader users know what the image conveys. Google also uses it as context for image search. Both of those are true and only the first one is the reason.
Good alt text describes what the image shows in the context of the page: "Chef plating a margherita pizza at Giuseppe's in Las Cruces." Bad alt text is a filename, an empty string on an informative image, or a keyword list. If the image is purely decorative, use an empty alt attribute so screen readers skip it. An empty alt on a decorative image is correct. A missing alt attribute is not the same thing and is not correct.
7. Compress before it reaches your pipeline
A CDN image service will happily resize a 10 MB source file, and the first visitor who triggers that transform pays for it. Compress at the source. Squoosh for one-offs, Sharp for automation, ImageOptim for a folder.
Treat file size targets as a smell test, not a rule. If a hero is over 500 KB after compression, something is wrong, usually a PNG that should have been a photo format or an image exported at 3x the size it displays.
8. Use a CDN for delivery
Images served from your origin hit your server on every cache miss. A CDN caches at the edge and serves from a location near the user. Cloudflare Images, Cloudinary, Imgix, and Vercel image optimization all do this. If you are on AWS Amplify, CloudFront is already underneath, and Next.js image optimization works without extra setup, though remote sources need to be listed in the remotePatterns config.
We are not going to tell you a CDN will make your site "twice as fast." It reduces latency and origin load. Measure it.
9. Use SVG for logos, icons, and diagrams
Vector, scales cleanly, usually tiny. Inline the critical ones to save a request. Do not use SVG for photographs or complex gradients, because the file size grows past what a raster format would have cost.
10. Audit what you actually ship
Chrome DevTools, Network tab, filter by Img, sort by size. Anything over 500 KB deserves an explanation. Our free SEO audit loads your page in Chromium and inspects the rendered DOM, including image formats, dimensions, and alt coverage.
What we are not claiming
An earlier version of this post ended with a list of promised outcomes: 60 to 80 percent smaller page weight, 2 to 4 seconds faster LCP, 5 to 15 Lighthouse points, better image search rankings. We made those numbers up. They were plausible, they were not measured, and we should not have printed them.
What we can say honestly is this. Images are often the largest byte category on a page. Oversized images can be a component of a slow LCP, and they are frequently not the whole story, because LCP is a composite of server time, discovery delay, download time, and render delay. Fixing images fixes the download component. If your LCP problem lives in server time or in a JavaScript animation, a perfectly compressed hero image will change nothing at all. Measure first. Our post on how to fix LCP on mobile walks the split.
If you want us to run the image pipeline work, see website speed optimization, or run the free audit first and see what your own numbers say.

Written by
Joshua R. GutierrezSEO Engineer, Axion Deep Digital
SEO strategist and full-stack engineer who builds the audit tooling, then does the work. Technical SEO, Core Web Vitals, and content systems for SaaS and B2B.
View full profile & credentials →Ready to build a website that performs?
Let us audit your current site, identify the biggest opportunities, and build a plan to grow your traffic and leads.