Image & Video Optimization for SEO
Last updated: January 6, 2026
Unoptimized media is the leading cause of poor Core Web Vitals and SEO rankings. This guide covers the complete media optimization stack—from Astro's build-time image processing to Cloudflare's edge-network video transformations—with specific techniques for file organization, format selection, and mobile delivery.
In This Guide
The direct link between media performance and search rankings
What happens if I skip media optimization?Real-world consequences of serving unoptimized images and videos
How should I organize media files in my project?Co-location vs cloud storage strategies for different asset types
How does Astro optimize images at build time?Using the Image component for automatic format conversion and resizing
Which image format should I use?JPEG vs PNG vs WebP vs AVIF—compression, quality, and browser support
How do I optimize videos with Cloudflare?Edge-network video transformations for mobile delivery
Which video codec should I use?H.264 vs VP9 vs AV1—encoding efficiency and compatibility tradeoffs
Which service should I use for different media types?Decision framework for choosing the right optimization approach
How do I implement a complete media optimization workflow?Step-by-step checklist for production-ready media delivery
Why does media optimization matter for SEO?
Media files—images and videos—typically account for 50-80% of a webpage’s total size. When these files aren’t optimized, the consequences directly impact your search rankings:
Core Web Vitals dependency. Google’s page experience signals include Largest Contentful Paint (LCP), which measures how quickly your largest visible element loads. For most pages, that element is a hero image or video. An unoptimized 2MB hero image can push LCP beyond the 2.5-second threshold, moving your page from “good” to “needs improvement” in Search Console.
Mobile-first indexing. Google primarily uses your mobile site for ranking. A 10MB video that loads acceptably on desktop fiber will devastate mobile users on 4G connections. Since mobile-first indexing became the default in 2020, mobile performance directly determines desktop rankings too.
Crawl budget efficiency. Search engine crawlers allocate limited resources to each site. Large media files consume bandwidth that could be spent discovering and indexing new content. Sites with bloated media often see slower indexation of new pages.
What happens if I skip media optimization?
Skipping media optimization creates compounding problems that extend beyond SEO:
User experience degradation
- Bounce rates increase 32% when page load time goes from 1 to 3 seconds (Google research)
- Mobile users on throttled connections see partially loaded pages or timeout errors
- Layout shift occurs as images load without reserved space, frustrating users mid-scroll
SEO ranking penalties
- Core Web Vitals failures demote your pages in search results
- Slow pages receive fewer crawls, delaying content updates in the index
- Competitors with faster sites capture rankings you could have earned
Infrastructure costs
- Bandwidth costs scale linearly with file sizes—a 5x larger image costs 5x more to serve
- CDN cache efficiency drops when serving unique, unoptimized variants
- Server resources are wasted processing requests for oversized files
Real-world example
A 4000x3000 pixel JPEG straight from a camera is typically 8-12MB. Served to a mobile user who only needs a 600px wide version, you’re transferring 20x more data than necessary. At scale, this means:
- A blog post with 5 unoptimized images: 40-60MB page weight
- Expected load time on 4G: 15-30 seconds
- Likely user behavior: Abandon before content loads
How should I organize media files in my project?
The right file organization strategy depends on the asset type and how it’s used. Here’s the architecture we recommend:
| Asset Type | Location | Reason |
|---|---|---|
| Blog photos | src/content/blog/[post-name]/ | Enables Astro’s astro:assets for optimization and type-safe frontmatter |
| UI icons/logos | src/assets/ | Global assets that need optimization and content hashing |
| Hero images | src/assets/ or Cloudflare Images | High-priority images benefit from both build-time and CDN optimization |
| Videos (8s+) | Cloudflare R2 or public/ | Large files (10MB+) bypass the build process to keep deployments fast |
Why co-location matters for images
Storing images alongside content (in src/content/) provides several advantages:
- Type safety — Astro validates image references at build time, catching broken paths before deployment
- Automatic optimization — Images processed through
astro:assetsget format conversion, resizing, and lazy loading - Content portability — Moving a blog post moves its images too, maintaining referential integrity
Why videos should live externally
Videos fundamentally differ from images in the build pipeline:
- Build time impact — A 50MB video processed at build time adds minutes to every deployment
- No build-time optimization — Astro doesn’t transcode video; it just copies files to
dist/ - Dynamic delivery needs — Videos benefit from adaptive bitrate streaming, which requires runtime processing
How does Astro optimize images at build time?
Astro’s built-in <Image /> component transforms images during the build step, producing optimized output without runtime overhead.
What the Image component does
When you use <Image />, Astro:
- Converts formats — Automatically transforms JPEGs and PNGs to WebP or AVIF (up to 80% smaller)
- Resizes physically — Shrinks dimensions based on the
widthattribute, not just CSS scaling - Generates responsive variants — Creates multiple sizes for
srcsetwhen configured - Adds lazy loading — Injects
loading="lazy"anddecoding="async"by default - Caches intelligently — Only re-processes images when content or attributes change (stored in
.astro/)
Implementation example
---
import { Image } from 'astro:assets';
import heroPhoto from './images/hiking-adventure.jpg';
---
<Image
src={heroPhoto}
alt="Hikers ascending a mountain trail at sunrise"
width={800}
quality={80}
/>
This produces HTML like:
<img
src="/_astro/hiking-adventure.hash.webp"
alt="Hikers ascending a mountain trail at sunrise"
width="800"
height="533"
loading="lazy"
decoding="async"
/>
Key configuration options
| Attribute | Purpose | Recommendation |
|---|---|---|
width | Target display width in pixels | Set to largest display size needed |
quality | Compression level (1-100) | 75-85 balances quality and size |
format | Output format (webp, avif, png, jpg) | Let Astro auto-select or prefer AVIF |
loading | Loading strategy | Keep “lazy” except for above-fold images |
When to use loading="eager"
For hero images and other above-the-fold content, lazy loading can actually hurt LCP. Override the default:
<Image
src={heroPhoto}
alt="Hero banner"
width={1200}
loading="eager"
/>
Which image format should I use?
Choosing the right image format can reduce file sizes by 30-80% with no visible quality loss. Here’s how modern formats compare:
Format comparison table
| Format | Best For | Compression | Browser Support | Transparency |
|---|---|---|---|---|
| JPEG | Photos, gradients | Lossy, 10:1 typical | Universal (100%) | No |
| PNG | Graphics, screenshots, transparency | Lossless | Universal (100%) | Yes |
| WebP | All images (replacement for JPEG/PNG) | Lossy or lossless, 25-35% smaller than JPEG | 97%+ (all modern browsers) | Yes |
| AVIF | All images (next-gen) | Lossy or lossless, 50%+ smaller than JPEG | 93%+ (Chrome, Firefox, Safari 16+) | Yes |
JPEG: The legacy standard
How it works: JPEG uses Discrete Cosine Transform (DCT) compression that discards visual information humans are less likely to notice. Quality settings from 1-100 control how aggressively data is discarded.
When to use it:
- Fallback for ancient browsers
- When source images are already JPEG (re-encoding introduces generation loss)
When to avoid it:
- Text, logos, or sharp edges (compression artifacts are visible)
- Images requiring transparency
Typical savings: A 2MB camera JPEG compressed to quality 80 yields ~400KB with acceptable visual quality.
PNG: Lossless but large
How it works: PNG uses DEFLATE compression (like ZIP files) that preserves every pixel exactly. PNG-8 uses a 256-color palette; PNG-24/32 supports millions of colors with alpha transparency.
When to use it:
- Screenshots with text
- Logos and icons requiring crisp edges
- Images with transparency (before WebP/AVIF support was widespread)
When to avoid it:
- Photographs (file sizes are 5-10x larger than JPEG)
- Any image where exact pixel preservation isn’t required
Optimization tip: Tools like pngquant can reduce PNG file sizes by 70% using lossy palette reduction while maintaining visual quality.
WebP: The practical choice
How it works: Developed by Google, WebP uses VP8 video codec techniques for lossy compression and a custom predictor for lossless. It supports both static images and animation.
When to use it:
- Default format for most web images in 2024+
- Replacing both JPEG and PNG in a single format
- Animated images (replaces GIF with 60%+ smaller files)
Key benefits:
- 25-35% smaller than JPEG at equivalent visual quality
- 26% smaller than PNG for lossless compression
- Supports transparency (unlike JPEG)
- Near-universal browser support (97%+)
Implementation: Astro automatically generates WebP when you don’t specify a format:
<Image src={photo} alt="Description" width={800} />
<!-- Outputs: .webp file -->
AVIF: The future standard
How it works: Based on the AV1 video codec (developed by the Alliance for Open Media including Google, Apple, Microsoft, and Netflix), AVIF achieves the highest compression ratios currently available.
When to use it:
- Performance-critical pages where every KB matters
- Sites where 93%+ browser support is acceptable
- Hero images and LCP elements
Key benefits:
- 50%+ smaller than JPEG at equivalent quality
- 20% smaller than WebP in most tests
- Excellent handling of fine details and gradients
- Supports HDR and wide color gamut
Trade-offs:
- Slower encoding (2-5x longer build times than WebP)
- Slightly lower browser support than WebP (no IE11, older Safari)
- Higher decode CPU usage on older mobile devices
Implementation with fallback:
<picture>
<source srcset="/image.avif" type="image/avif">
<source srcset="/image.webp" type="image/webp">
<img src="/image.jpg" alt="Description" width="800" height="600">
</picture>
Real-world file size comparison
For a typical 1920x1080 photograph:
| Format | Quality Setting | File Size | Relative Size |
|---|---|---|---|
| JPEG (original) | 100 | 1.2 MB | 100% |
| JPEG (optimized) | 80 | 320 KB | 27% |
| PNG (lossless) | N/A | 4.8 MB | 400% |
| WebP | 80 | 240 KB | 20% |
| AVIF | 65 | 145 KB | 12% |
Recommendation
Default to WebP for maximum compatibility. Use AVIF with WebP fallback for performance-critical pages. Reserve JPEG only for legacy system compatibility, and PNG only when lossless is truly required.
How do I optimize videos with Cloudflare?
Astro does not process video files. To prevent a 10MB video from stalling on mobile connections, use Cloudflare’s edge-network transformations.
The problem with unoptimized video
Standard Cloudflare caching (Free or Pro plans) serves the original file from edge servers—faster than origin, but still the full file size. A 1080p MP4 that’s 50MB remains 50MB regardless of the viewer’s device.
The solution: URL-based transformations
Cloudflare Media Transformations (available as an add-on or with certain plans) let you request optimized versions via URL parameters:
---
// Original video hosted on R2 or your domain
const rawVideo = "https://yourdomain.com/videos/product-demo.mp4";
// Optimized for mobile via Cloudflare Edge
// width=600: Resizes the video dimensions
// format=auto: Serves the best codec (AV1/VP9/H264) for the browser
const mobileVideo = `https://yourdomain.com/cdn-cgi/media/width=600,format=auto/${rawVideo}`;
---
<video autoplay loop muted playsinline>
<source src={mobileVideo} type="video/mp4">
Your browser does not support the video tag.
</video>
Available transformation parameters
| Parameter | Effect | Example |
|---|---|---|
width | Resize to specified width, maintaining aspect ratio | width=600 |
height | Resize to specified height | height=400 |
format | Convert to specific codec or auto-select | format=auto |
quality | Compression level | quality=80 |
Responsive video delivery
For truly adaptive delivery, serve different resolutions based on viewport:
---
const baseUrl = "https://yourdomain.com/cdn-cgi/media";
const videoPath = "videos/product-demo.mp4";
const sources = {
mobile: `${baseUrl}/width=480,format=auto/${videoPath}`,
tablet: `${baseUrl}/width=768,format=auto/${videoPath}`,
desktop: `${baseUrl}/width=1280,format=auto/${videoPath}`,
};
---
<video autoplay loop muted playsinline id="hero-video">
<source src={sources.mobile} type="video/mp4" media="(max-width: 640px)">
<source src={sources.tablet} type="video/mp4" media="(max-width: 1024px)">
<source src={sources.desktop} type="video/mp4">
</video>
Which video codec should I use?
Video codecs determine how efficiently your video files compress and how broadly they play across devices. The choice impacts both file size and battery drain on mobile.
Codec comparison table
| Codec | Container | Compression Efficiency | Browser Support | Hardware Decode |
|---|---|---|---|---|
| H.264 (AVC) | MP4 | Baseline (1x) | Universal (100%) | Universal |
| H.265 (HEVC) | MP4 | 25-50% better than H.264 | Limited (Safari, some Edge) | Apple devices, newer Android |
| VP9 | WebM | 30-50% better than H.264 | 95%+ (Chrome, Firefox, Edge) | Limited mobile support |
| AV1 | MP4, WebM | 30-50% better than VP9 | 85%+ (Chrome, Firefox, Safari 17+) | Newer devices (2020+) |
H.264: Universal compatibility
How it works: H.264 (also called AVC or MPEG-4 Part 10) uses block-based motion compensation and has been the web video standard since 2010.
When to use it:
- Maximum device compatibility is required
- Targeting older mobile devices or smart TVs
- Fallback source for progressive enhancement
When to avoid it:
- Bandwidth-constrained applications
- When 85%+ browser support (AV1) is acceptable
Encoding recommendation: Use CRF (Constant Rate Factor) 23-28 for web delivery:
ffmpeg -i input.mov -c:v libx264 -crf 23 -preset slow -c:a aac -b:a 128k output.mp4
H.265 (HEVC): Apple ecosystem
How it works: HEVC doubles the compression efficiency of H.264 using larger coding blocks and more sophisticated prediction modes.
When to use it:
- Safari-only applications
- iOS/macOS native apps
- When licensing costs aren’t a concern
Major limitation: HEVC requires patent licensing, leading to inconsistent browser support. Chrome and Firefox don’t support it due to royalty requirements.
Not recommended for general web use due to fragmented browser support.
VP9: Google’s alternative
How it works: VP9 is Google’s royalty-free codec designed to compete with HEVC. YouTube uses VP9 extensively, proving its web viability.
When to use it:
- YouTube-style video delivery
- When you need better compression than H.264 but can’t use AV1
- Chrome/Firefox-primary audiences
Key benefits:
- 30-50% smaller than H.264 at equivalent quality
- Royalty-free (no licensing costs)
- Proven at scale (YouTube serves petabytes daily)
Trade-offs:
- Slower encoding than H.264 (2-4x)
- Limited hardware decode on mobile (software decode drains battery)
- No Safari support
Encoding recommendation:
ffmpeg -i input.mov -c:v libvpx-vp9 -crf 30 -b:v 0 -c:a libopus -b:a 128k output.webm
AV1: The future of web video
How it works: Developed by the Alliance for Open Media (Google, Apple, Microsoft, Netflix, Amazon), AV1 combines the best techniques from VP9, HEVC, and Daala.
When to use it:
- Performance-critical video delivery
- Modern browser audiences (85%+ support in 2024)
- When encoding time isn’t a bottleneck
Key benefits:
- 30-50% smaller than VP9 (50-70% smaller than H.264)
- Royalty-free
- Excellent detail preservation at low bitrates
- Hardware decode support growing rapidly
Trade-offs:
- Very slow encoding (10-100x slower than H.264)
- Limited hardware decode on older devices
- Safari support only in version 17+ (2023)
Encoding recommendation: Use SVT-AV1 for faster encoding:
ffmpeg -i input.mov -c:v libsvtav1 -crf 30 -preset 6 -c:a libopus -b:a 128k output.mp4
Real-world bitrate comparison
For 1080p video at equivalent visual quality:
| Codec | Bitrate Required | File Size (1 min) | Relative Size |
|---|---|---|---|
| H.264 | 5 Mbps | 37.5 MB | 100% |
| VP9 | 3 Mbps | 22.5 MB | 60% |
| AV1 | 2 Mbps | 15 MB | 40% |
Serving multiple codecs
For maximum compatibility with minimum bandwidth, serve AV1 with fallbacks:
<video controls playsinline>
<!-- Best compression, modern browsers -->
<source src="video.av1.mp4" type="video/mp4; codecs=av01.0.05M.08">
<!-- Good compression, wide support -->
<source src="video.vp9.webm" type="video/webm; codecs=vp9">
<!-- Universal fallback -->
<source src="video.h264.mp4" type="video/mp4">
</video>
Recommendation
Use H.264 as your baseline for universal compatibility. Add AV1 as the primary source for modern browsers to cut bandwidth 50-60%. Skip VP9 in new projects—AV1 has surpassed it in both efficiency and support.
Which service should I use for different media types?
Choose your optimization approach based on the asset type and your hosting setup:
| Goal | Tool | Plan Required |
|---|---|---|
| Small blog images (< 500KB source) | Astro <Image /> | Free (build-time) |
| UI polish and global CDN | Cloudflare Images | Pro ($20/mo) or Images subscription |
| Short looping videos (< 30s) | Cloudflare Media Transformations | Add-on (usage-based) |
| Long-form video with player | Cloudflare Stream | $5/mo minimum |
| Video hosting without transformation | Cloudflare R2 + standard CDN | R2 pricing (storage + egress) |
Decision flowchart
-
Is it an image?
- Yes → Use Astro
<Image />for build-time optimization - Also want CDN variants? → Add Cloudflare Images
- Yes → Use Astro
-
Is it a video under 30 seconds?
- Yes → Host on R2, serve via Media Transformations
- No transformation budget? → Compress locally, serve from
public/
-
Is it a long-form video with playback controls?
- Yes → Use Cloudflare Stream for adaptive bitrate streaming
- Budget-conscious? → YouTube/Vimeo embed
How do I implement a complete media optimization workflow?
Follow this checklist to ensure your media delivery is production-ready:
Image workflow
- Store in
src/content/orsrc/assets/— Enable Astro’s optimization pipeline - Use
<Image />component — Never use raw<img>tags for local images - Set explicit dimensions — Provide
widthto enable proper resizing - Add descriptive alt text — Required for accessibility and image SEO
- Use
loading="eager"for hero images — Prevent LCP delays on above-fold content - Prefer AVIF format — When browser support allows (falls back to WebP)
Video workflow
- Upload to Cloudflare R2 — Or
public/if files are small (< 5MB) - Create optimization wrapper — Build a component that appends
cdn-cgi/media/parameters - Serve responsive variants — Different resolutions for mobile, tablet, desktop
- Add
playsinlineattribute — Required for autoplay on iOS - Include
mutedfor autoplay — Browsers block unmuted autoplay
Build pipeline
- Configure
.astro/caching — Ensure your CI/CD preserves the cache between builds - Set up image CDN — Cloudflare Images or similar for global distribution
- Monitor Core Web Vitals — Use Search Console and PageSpeed Insights regularly
Example CI/CD cache configuration (GitHub Actions)
- name: Cache Astro build
uses: actions/cache@v3
with:
path: |
.astro
node_modules/.astro
key: astro-${{ hashFiles('src/**/*.{png,jpg,jpeg,webp,avif}') }}
restore-keys: |
astro-
This caches processed images between builds, dramatically reducing build times for sites with many images.
Sources
- Astro Assets Documentation
- Cloudflare Images Documentation
- Cloudflare Media Transformations
- Cloudflare Stream Documentation
- Google Core Web Vitals
- Google PageSpeed Insights
Looking for expert guidance? Schedule a free consult:
Book a Free Consultation