OctaneAudit
Guides

Image SEO: Alt Text, Lazy Loading, and WebP Done Right

Image SEO optimization comes down to three jobs: describe each image, load it at the right moment, and ship it in a modern format at the right size.

OCOctaneAudit Team10 min read
Guides

Image SEO: Alt Text, Lazy Loading, and WebP Done Right

Images are usually the heaviest part of a page and the least described. That combination hurts twice: slow pages lose Core Web Vitals, and undescribed images lose visibility in Google Images and in the image packs that appear in regular results.

Image SEO optimization is not one setting. It is three separate jobs, and most sites get at least one of them wrong:

  1. Describe the image so search engines and screen readers understand it.
  2. Load it at the right moment, neither too early nor too late.
  3. Deliver it in a modern format at the size it is actually displayed.

This guide covers each job, then the discoverability details (sitemaps, structured data, robots) that decide whether Google can find your images at all.

Job 1: Describe the image

Google uses several sources to understand an image: the alt attribute, the surrounding text and headings, the page title, the file name, and its own computer vision. Alt text is the one you control most directly, and it doubles as an accessibility requirement.

Writing alt text that works

Good alt text describes what the image shows in the context of the page. It is written for a person who cannot see the image, not for a ranking algorithm.

ImageWeak alt textBetter alt text
Product photoshoesBrown leather Chelsea boot, side view
Chart in a reportchartBar chart of mobile LCP by template, product pages slowest
Team phototeam photo seo agency best seo servicesFive team members standing outside the office
Decorative dividerdivider lineEmpty: alt=""
Logo linking homelogoAcme Outdoor home

A few rules cover most cases:

  • Be specific. Color, material, angle and subject beat a single generic noun.
  • Keep it to a sentence. There is no official character limit, but screen reader users hear every word. If the image needs a paragraph, put the paragraph in the visible caption or body text.
  • Do not stuff keywords. Repeating the page keyword in every alt attribute is a spam signal and makes the page worse for assistive technology users.
  • Use empty alt for decoration. alt="" tells screen readers to skip the image. Omitting the attribute entirely is different: many screen readers then announce the file name.
  • Linked images describe the destination. When an image is the only content of a link, its alt text acts as the anchor text.
<figure>
  <img src="/img/chelsea-boot-brown-side.webp"
       alt="Brown leather Chelsea boot, side view"
       width="1200" height="900">
  <figcaption>The Ridgeway boot in walnut leather.</figcaption>
</figure>

File names and surrounding text

File names give Google a light hint, so chelsea-boot-brown-side.webp is better than IMG_4821.webp. Do not rename an entire media library for this alone, but set a naming convention for new uploads.

Placement matters more. An image near a relevant heading and caption is easier to understand than the same image in a generic carousel at the bottom of the page.

Job 2: Load the image at the right moment

Lazy loading defers offscreen images until the user scrolls near them. Done right, it cuts initial page weight. Done wrong, it delays your largest image and fails Largest Contentful Paint.

Use native lazy loading

The browser attribute is supported in all current browsers and needs no JavaScript:

<img src="/img/gallery-3.webp" alt="Boot sole tread detail"
     width="800" height="600" loading="lazy" decoding="async">

Googlebot supports native lazy loading. It renders pages with a tall viewport rather than scrolling, so images marked loading="lazy" still load during rendering.

Never lazy-load the LCP image

The most common image mistake on modern sites is a theme or framework that adds loading="lazy" to every image, including the hero. The browser then waits for layout before it even requests the most important image on the page.

For the first visible image, do the opposite:

<img src="/img/hero-1200.webp"
     srcset="/img/hero-640.webp 640w, /img/hero-1200.webp 1200w, /img/hero-1800.webp 1800w"
     sizes="100vw" width="1800" height="800"
     fetchpriority="high" alt="Hiker crossing a ridge at sunrise">

Our LCP playbook covers how to find the LCP element per template and measure load delay.

Reserve space to avoid layout shift

Always set width and height attributes, or a CSS aspect-ratio. The browser uses them to reserve the box before the file arrives, so content below does not jump. Lazy-loaded images without dimensions are a classic source of layout shift, covered in detail in understanding Cumulative Layout Shift.

img {
  max-width: 100%;
  height: auto; /* keeps the ratio from the width and height attributes */
}

Job 3: Deliver the right format at the right size

Choose the format

FormatBest forNotes
AVIFPhotos, large hero imagesUsually the smallest files; slower to encode; supported in current Chrome, Edge, Firefox and Safari
WebPPhotos and graphics, general defaultSupported in every current browser; fast to encode; supports transparency
JPEGFallback for photosUniversal, but larger than WebP or AVIF at similar quality
PNGScreenshots, images that need lossless qualityOften very large for photos; convert where possible
SVGLogos, icons, simple illustrationsResolution independent; minify and avoid embedded raster data

Google Search can index BMP, GIF, JPEG, PNG, WebP, SVG and AVIF, so moving to modern formats does not cost you eligibility in Google Images.

A practical default: WebP for everything, AVIF for large photographic images where the extra saving matters, with JPEG as the fallback only if you still support very old browsers.

Serve modern formats with a fallback

The picture element lets the browser pick the first format it supports:

<picture>
  <source type="image/avif" srcset="/img/boot-800.avif 800w, /img/boot-1200.avif 1200w" sizes="(min-width: 900px) 50vw, 100vw">
  <source type="image/webp" srcset="/img/boot-800.webp 800w, /img/boot-1200.webp 1200w" sizes="(min-width: 900px) 50vw, 100vw">
  <img src="/img/boot-1200.jpg" alt="Brown leather Chelsea boot, side view"
       width="1200" height="900" loading="lazy" decoding="async">
</picture>

The img inside carries the alt text, dimensions and loading attributes for every source.

If you cannot change markup, serve the format by content negotiation at the server. This Nginx example returns photo.jpg.webp when it exists and the browser accepts WebP:

map $http_accept $webp_suffix {
  default        "";
  "~*image/webp" ".webp";
}

server {
  location ~* ^/images/.+\.(png|jpe?g)$ {
    add_header Vary Accept;
    try_files $uri$webp_suffix $uri =404;
  }
}

The Vary: Accept header is essential. Without it, a CDN may cache the WebP response and serve it to a client that asked for JPEG.

Convert in bulk

For a one-off migration, the reference encoders are fine:

# WebP at quality 80
cwebp -q 80 boot.jpg -o boot.webp

# AVIF with libavif
avifenc -q 60 boot.jpg boot.avif

In a build pipeline, a library such as sharp in Node.js does resizing and conversion in one pass:

import sharp from "sharp";

const widths = [640, 1200, 1800];
for (const w of widths) {
  await sharp("src/hero.jpg").resize(w).webp({ quality: 78 }).toFile(`public/img/hero-${w}.webp`);
  await sharp("src/hero.jpg").resize(w).avif({ quality: 55 }).toFile(`public/img/hero-${w}.avif`);
}

Image CDNs (Cloudinary, Imgix, Cloudflare Images and most framework image components) do the same on request, which is the easiest route for large catalogs.

Size images to their rendered width

Format is only half the saving. A 3000-pixel photo displayed at 400 pixels wide on a phone wastes most of its bytes regardless of format. Use srcset with three to five widths and a sizes attribute that matches your layout. Then check in DevTools: the Network panel shows which file the browser picked, and the Elements panel shows the rendered size. If the intrinsic width is more than about twice the rendered width at 2x pixel density, your sizes value is wrong.

Make images discoverable

A perfectly described, perfectly compressed image still needs Google to find it.

Use real image elements

Google does not index images set as CSS backgrounds. If an image carries meaning (a product, a diagram, a photo you want in Google Images), it must be an img element, optionally inside picture. Keep CSS backgrounds for purely decorative textures.

Do not block image paths

Check robots.txt for rules that block /images/, /media/, /uploads/ or your CDN hostname. Googlebot-Image follows the same file. If your images live on a separate host such as cdn.example.com, that host needs its own permissive robots.txt. Our guide to robots.txt versus noindex covers how these rules interact.

Image sitemaps

For images that are loaded in ways Google might miss, or to speed up discovery of a large catalog, add image entries to your XML sitemap:

<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
        xmlns:image="http://www.google.com/schemas/sitemap-image/1.1">
  <url>
    <loc>https://www.example.com/boots/ridgeway</loc>
    <image:image>
      <image:loc>https://cdn.example.com/img/ridgeway-side.webp</image:loc>
    </image:image>
    <image:image>
      <image:loc>https://cdn.example.com/img/ridgeway-sole.webp</image:loc>
    </image:image>
  </url>
</urlset>

Google deprecated the image:caption, image:title, image:geo_location and image:license tags in 2022. Only image:loc is needed.

Structured data and previews

Product, Article and Recipe structured data all accept an image property, and rich results often require one. Provide several aspect ratios for the best chance of display:

{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "Ridgeway Chelsea Boot",
  "image": [
    "https://cdn.example.com/img/ridgeway-1x1.webp",
    "https://cdn.example.com/img/ridgeway-4x3.webp",
    "https://cdn.example.com/img/ridgeway-16x9.webp"
  ]
}

Two related settings are worth checking:

  • Large previews. The robots meta value max-image-preview:large allows Google to show large image previews, which matters for Google Discover. Make sure no plugin has set it to none or standard.
  • Social images. og:image does not affect Google rankings, but it controls how links look when shared. Use a 1200 by 630 pixel image that exists and returns 200.

A quick image audit checklist

Run these checks per template (home, category, product, article), not per page:

  1. Identify the LCP image. Confirm it is not lazy-loaded and has fetchpriority="high".
  2. List images without width and height. Each one is a potential layout shift.
  3. Filter the Network panel by images and sort by size. Anything over about 200 KB on mobile deserves a look.
  4. Check formats. JPEG and PNG photos are candidates for WebP or AVIF.
  5. Sample alt text: missing, generic, keyword-stuffed, or file names used as alt.
  6. Confirm meaningful images are img elements, not CSS backgrounds.
  7. Test image URLs against robots.txt, including any CDN hostname.
  8. Inspect a page in Search Console and check the rendered HTML contains final image URLs.

Image regressions creep back in with every new campaign banner and plugin update. If you want this checked every day instead of once a quarter, AI Monitoring flags new oversized images, missing dimensions and LCP changes as they happen.

Summary

  • Alt text describes the image for people first; keep it specific, short and free of keyword stuffing, and use alt="" for decoration.
  • Lazy-load offscreen images with the native attribute, never the LCP image, and always reserve space with dimensions.
  • Serve WebP by default and AVIF for large photos, sized to the rendered width with srcset and sizes.
  • Meaningful images must be real img elements on crawlable paths; image sitemaps need only image:loc.
  • Audit by template, because one theme setting usually controls hundreds of images.
SharePostLinkedInFacebook

Get Your SEO Audit

Every issue in this article, checked on your site by an auditor and delivered as a prioritized roadmap within 48-72 hours.

See pricing

Related articles