Overview

Images are ranking signals, layout-shift hazards, and the largest single contributor to page weight. Get the seven attributes right (filename, alt, width, height, format, loading, sitemap) and the image earns rich-result thumbnails, contributes to ranking, and stops breaking CLS. Get any one wrong and the image either fails to rank or drags the page down.

Name image files like search queries

Filenames are read by Google Image Search and used as a secondary relevance signal. Treat them like a second alt text.

  • Use kebab-case descriptive names: postgres-replication-architecture.png, not IMG_4827.png or screenshot-final-v2.png.
  • Include the topic keyword, not the page slug. The image may be reused on multiple pages.
  • Strip metadata and camera EXIF noise. exiftool -all= clears it.
  • Folder structure matters too. /images/postgres/replication-architecture.png reads better than /uploads/2026/05/file.png.

Write descriptive alt text, never stuff keywords

Alt text serves screen readers first and search engines second. The signals overlap; write for the screen reader.

  • Describe what the image shows, not what page it lives on. “Diagram of Postgres logical replication with primary and replica” beats “Postgres image.”
  • 8 to 15 words is the sweet spot. Past 25 words, screen readers truncate.
  • Decorative images use empty alt: alt="". Do not omit the attribute; an absent alt is treated as missing.
  • Never repeat the caption verbatim; the screen reader announces both.
  • Never keyword-stuff: “Postgres Postgres database Postgres SQL Postgres replication” is a manual-action trigger.

Always set width and height to prevent CLS

Cumulative Layout Shift is the cheapest Core Web Vital to fix; reserve space.

<img
  src="/images/postgres-replication.avif"
  width="1200"
  height="630"
  alt="Postgres logical replication architecture"
  loading="lazy"
/>
  • Set the intrinsic dimensions, not the rendered size. CSS resizes responsively; the attributes reserve the box.
  • Or set CSS aspect-ratio: 1200 / 630. Either works; both is redundant.
  • A late-loading image without reserved dimensions is the most common CLS killer. See the CLS rules in core-web-vitals.

Ship AVIF and WebP with a JPEG fallback

Modern formats cut bytes by 30 to 70 percent at the same visual quality. Browser support is over 95% as of 2025.

<picture>
  <source srcset="/images/hero.avif" type="image/avif" />
  <source srcset="/images/hero.webp" type="image/webp" />
  <img src="/images/hero.jpg" width="1200" height="630" alt="Hero" />
</picture>
  • AVIF first: 50% smaller than WebP at the same quality, slower to encode.
  • WebP second: universal support, fast encoding.
  • JPEG fallback: photos. PNG fallback: graphics with hard edges.
  • Compress to visually lossless. cwebp -q 80 and avifenc --speed 6 --min 30 --max 50 are reasonable defaults.

Lazy-load below the fold, eager-load the LCP image

Native browser lazy loading is universal; use it.

  • Every image below the fold: loading="lazy".
  • The LCP image (hero, first article image): loading="eager" plus fetchpriority="high" plus a <link rel="preload"> in <head>.
  • Iframes: loading="lazy" works the same way.
  • Do not lazy-load images that are 100% visible on initial render; the lazy-load overhead delays LCP.

Submit an image sitemap

Image sitemaps surface images that JavaScript-rendered pages might hide from crawlers.

<url>
  <loc>https://example.com/seo/image-seo</loc>
  <image:image>
    <image:loc>https://example.com/images/image-seo-hero.avif</image:loc>
    <image:caption>Image SEO hero diagram</image:caption>
  </image:image>
</url>
  • Use the xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" extension on the main sitemap.
  • Include only images on indexable canonical pages.
  • Regenerate on every build; never hand-edit.

Earn rich-result thumbnails with a high-res image and matching schema

Rich results (article cards, recipe cards, video cards) require a <meta property="og:image"> and a JSON-LD image field that both point at a high-resolution image.

  • Minimum 1200 pixels on the long edge. Smaller images are disqualified from rich results.
  • Three aspect ratios for Article: 1x1, 4x3, 16x9. Provide all three in the JSON-LD image array if the schema is Article or BlogPosting. See structured-data.
  • The OG image and the JSON-LD image should match. See the OG image rules in og-images.

Set explicit Cache-Control on image URLs

Repeat visitors and CDN edges depend on cache headers; the default is browser-dependent.

  • Hashed filenames (/images/hero-8f3a.avif) ship with Cache-Control: public, max-age=31536000, immutable.
  • Non-hashed filenames need a shorter TTL plus stale-while-revalidate=86400 so updates propagate without breaking the cache hit rate.
  • Set the headers at the CDN, not in the application. Cloudflare Cache Rules do this with a single rule; see cloudflare for the wider setup.