Image SEO: file names, size, formats and lazy loading

·4 min read

Image SEO is making your images findable by Google and cheap for the browser to load. Put each image in a real <img src> with a short descriptive file name, serve WebP or AVIF at roughly the size it is shown, give it width and height, add srcset for large images, and lazy-load everything except the main image near the top. Alt text matters too, and it has its own guide on writing image alt text.

Most sites get the discovery part right by accident and the weight part wrong on purpose, because a 4 MB hero photo looked fine on the designer's fiber connection.

How Google discovers images

Google finds images in the src attribute of an <img> element, including an <img> inside <picture>. Its Google Images guidelines say plainly that Google doesn't index CSS images. A product photo set as a background-image will never show up in Google Images, whatever you name it.

The supported formats are BMP, GIF, JPEG, PNG, WebP, SVG and AVIF. Google also asks you to reference the same image by the same URL everywhere, so it can cache it instead of fetching copies.

Two common ways to hide images by mistake:

  • Putting the real URL only in data-src and swapping it in with a script. Google can render that, but a crawler that doesn't run JavaScript sees a placeholder. Our JavaScript SEO guide covers which crawlers render.
  • Using srcset without src. Google recommends always giving a fallback in src, because some browsers and crawlers don't read srcset.

Image file names and image sitemaps

File names are a small signal, and they cost nothing to get right on new uploads. Google's own example is that my-new-black-kitten.jpg beats IMG00023.JPG. Use lowercase words separated by hyphens and describe the subject, such as red-trail-running-shoe.jpg.

Don't rename thousands of existing files for this. Every renamed URL loses whatever the old one had built up in Google Images unless you redirect it, and the gain is small.

An image sitemap helps when Google can't find images through your HTML, for example on JavaScript galleries. Only two tags still count, <image:image> and <image:loc>. Google dropped caption, title, geo_location and license from its image sitemap docs, so leave them out. Each <url> can list up to 1,000 images, and <image:loc> may point to a CDN on another domain.

<url>
  <loc>https://example.com/shoes/trail-runner</loc>
  <image:image>
    <image:loc>https://cdn.example.com/red-trail-running-shoe.avif</image:loc>
  </image:image>
</url>

The namespace and the rest of the file format are in our XML sitemap guide.

WebP, AVIF and image file size

Serve WebP or AVIF for photos and keep JPEG or PNG as the fallback. Both newer formats are usually much smaller than JPEG at the same visible quality, and every current browser supports WebP. AVIF compresses harder but encodes slower, so many sites let the CDN pick per request.

<picture>
  <source srcset="/shoe-960.avif" type="image/avif">
  <source srcset="/shoe-960.webp" type="image/webp">
  <img src="/shoe-960.jpg" alt="Red trail running shoe, side view" width="960" height="640">
</picture>

Format is only half of it. The bigger waste is pixels nobody sees. A 4000-pixel camera export shown in a 600-pixel column downloads every pixel and then gets scaled down. Resize to about twice the displayed width for sharp screens and compress from there. If an image tops a few hundred kilobytes, look at it. A large animated GIF should usually be a video.

Width, height and srcset to avoid layout shift

Set width and height on every <img>. The browser uses their ratio to reserve space before the file arrives, so text below doesn't jump when the image loads. That jump is what Cumulative Layout Shift measures. Keep CSS height: auto so the image still scales.

For any image wider than a phone screen, add srcset with a few widths and a sizes attribute that says how wide it displays:

<img src="/hero-960.webp"
     srcset="/hero-480.webp 480w, /hero-960.webp 960w, /hero-1600.webp 1600w"
     sizes="(min-width: 1024px) 50vw, 100vw"
     width="1600" height="900" alt="...">

Don't skip sizes. With width descriptors and no sizes, the browser assumes the image fills the viewport and often picks a bigger file than it needs.

Lazy loading without lazy-loading the LCP image

Use native loading="lazy" on images below the fold and nowhere else. web.dev's lazy-loading guide says not to lazy-load images likely to be in the viewport on load, especially the LCP image. A lazy main image waits for layout before the browser even requests it, which pushes back Largest Contentful Paint.

The hero gets the opposite treatment:

<img src="/hero-960.webp" fetchpriority="high" width="1600" height="900" alt="...">

Native lazy loading keeps the URL in src, so every crawler still sees it. That is why it beats script libraries that park the URL in data-src. Many CMS themes add loading="lazy" to every image by default, including the first one, so check the hero by hand. If LCP is still slow after that, how to improve LCP breaks the time down by subpart.

Run an image SEO check on your page

Our Image SEO Checker fetches one URL and checks every <img> in the HTML for alt text, width and height, srcset without sizes, script-only loading and generic file names like IMG_0042.jpg. It picks the first large image as the likely LCP image and flags it if it is lazy-loaded, loaded by script, or missing fetchpriority="high" and a preload.

It then downloads the first 12 image files to find broken links, JPEG, PNG or GIF files that the server sent although the request accepted WebP and AVIF, files over 300 KB, and files much wider than their width attribute. It reads the server HTML only, so images a script adds after load and CSS background images are not checked. It reports problems and does not compress or convert files. A run costs 10 credits.

Keep reading