Website Speed: Why Images Are Killing It (And How to Fix)
I fixed 50 slow websites. Images were always the problem. Here's the complete optimization workflow.
Website Speed: Why Images Are Killing It
Every slow website I've audited has the same problem: images.
The Statistics
| Issue | Frequency | Impact |
|---|---|---|
| Images too large | 95% of sites | Catastrophic |
| Wrong format | 70% | Major |
| No compression | 50% | Major |
| No lazy loading | 60% | Moderate |
| Wrong size | 80% | Moderate |
Why Images Slow Sites
Images are typically 60-80% of page weight.
A 3MB page with images becomes 300KB optimized.
Impact:
- 3G user: 18 seconds → 1.5 seconds
- Mobile bounce: 75% → 35%
- SEO rankings: Drop without Core Web Vitals
The Optimization Workflow
Step 1: Audit
Run PageSpeed Insights on your site: pagespeed.web.dev
Look for:
- "Properly size images"
- "Serve images in next-gen formats"
- "Defer offscreen images"
Step 2: Identify Problem Images
In DevTools:
- Open page
- Network tab
- Filter "Img"
- Sort by size (largest first)
- Note top 10 offenders
Step 3: Optimize
Compress Existing Images
from PIL import Image
from pathlib import Path
def optimize_site_images(image_dir, output_dir, max_size=1920, quality=85):
"""Optimize all site images."""
input_path = Path(image_dir)
output_path = Path(output_dir)
output_path.mkdir(exist_ok=True)
extensions = ('.jpg', '.jpeg', '.png')
for img_file in input_path.glob('*'):
if img_file.suffix.lower() not in extensions:
continue
img = Image.open(img_file)
# Resize if needed
if max(img.size) > max_size:
ratio = max_size / max(img.size)
img = img.resize(
tuple(int(d * ratio) for d in img.size),
Image.Resampling.LANCZOS
)
# Convert to WebP
if img.mode == 'RGBA':
img = img.convert('RGB')
output = output_path / f"{img_file.stem}.webp"
img.save(output, 'WEBP', quality=quality)
orig = img_file.stat().st_size / 1024
new = output.stat().st_size / 1024
if orig > new:
print(f"✓ {img_file.name}: {orig:.0f}KB → {new:.0f}KB")
optimize_site_images('./images', './optimized')
Step 4: Implement Lazy Loading
Add to HTML:
<!-- Native lazy loading -->
<img
src="image.jpg"
loading="lazy"
alt="Description"
>
For background images:
// Intersection Observer for background images
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
observer.unobserve(img);
}
});
});
document.querySelectorAll('[data-src]').forEach(img => {
observer.observe(img);
});
Step 5: Responsive Images
<img
src="hero-800.jpg"
srcset="
hero-400.jpg 400w,
hero-800.jpg 800w,
hero-1200.jpg 1200w,
hero-1920.jpg 1920w
"
sizes="100vw"
loading="lazy"
alt="Hero image"
>
WordPress Optimization
Plugins
1. ShortPixel Image Optimizer
- Auto-compress on upload
- WebP generation
- Bulk compression
2. Imagify
- Similar features
- Good WP Rocket integration
3. Cloudflare
- CDN + Polish
- Auto WebP
- Caching
Settings
ShortPixel:
- Compression: Glossy
- Create WebP: Yes
- Auto on upload: Yes
- EXIF: Remove
The Results
Before optimization:
- Homepage: 8.4 seconds load time
- Page weight: 5.2 MB
- Mobile bounce: 72%
- Core Web Vitals: Poor
After optimization:
- Homepage: 1.2 seconds load time
- Page weight: 680 KB
- Mobile bounce: 34%
- Core Web Vitals: Good
Common Mistakes
Mistake 1: Optimizing After Upload
Problem: Already serving slow images.
Solution: Optimize existing images, not just new uploads.
Mistake 2: Forgetting Thumbnails
Problem: WordPress creates multiple sizes, all unoptimized.
Solution: Bulk optimize all WordPress images.
Mistake 3: CSS Background Images
Problem: Lazy loading doesn't work on background images.
Solution: Use JavaScript intersection observer.
Mistake 4: No Fallback
Problem: WebP only, broken for some users.
Solution: Always provide JPEG fallback.
Quick Wins
- Compress all images: ShortPixel or Imagic AI
- Convert to WebP: 30% smaller
- Add lazy loading:
loading="lazy" - Responsive images: srcset attribute
- CDN: Cloudflare free tier
Tools
| Tool | Purpose |
|---|---|
| Imagic AI | Quick compression |
| PageSpeed Insights | Audit |
| ShortPixel | WordPress |
| Cloudflare | CDN |
| ImageMagick | CLI |
FAQ
Q: How small should images be?
A: Under 200KB for content, under 400KB for heroes.
Q: What's the biggest win?
A: Compressing + converting to WebP. 70-90% reduction.
Q: Does lazy loading help SEO?
A: Yes. Faster pages = better rankings.
Q: Should I use a CDN?
A: Yes. Especially for global sites. Cloudflare free tier is excellent.
The Bottom Line
- Audit first: Find problem images
- Compress: Use ShortPixel or Imagic AI
- Convert: WebP with JPEG fallback
- Lazy load:
loading="lazy" - Responsive: srcset for all devices
- CDN: Cloudflare for caching
Optimized 50+ slow sites. Questions? Ask below.