Fix Broken Images — Why Images Won't Display in Browser
The broken image icon is one of the most common web bugs. The image exists, the path looks right, but the browser shows nothing (or a broken icon). Here's a systematic debug checklist.
Common Error Messages
Check the browser console (F12) for these:
Failed to load resource: the server responded with a status of 404Failed to load resource: net::ERR_BLOCKED_BY_RESPONSEAccess to image from origin 'X' has been blocked by CORS policyThe image cannot be displayed because it contains errorsResource interpreted as Image but transferred with MIME type text/htmlContent Security Policy: img-src directive
Debug Checklist
1. Is the File Actually There? (404)
# Check if the URL returns the file
curl -I https://your-site.com/images/photo.jpg
# Expected: HTTP/2 200 and Content-Type: image/jpeg
# Problem: HTTP/2 404 → file doesn't exist at that path
Fix: Check file path, case sensitivity (Linux is case-sensitive!), and deployment.
2. Is the MIME Type Correct?
curl -I https://your-site.com/images/photo.avif
# Problem: Content-Type: application/octet-stream
# Fix: Content-Type: image/avif
Browsers won't render images with wrong MIME types. Modern formats (AVIF, WebP, HEIC) often have missing server config.
Fix (Nginx):
types {
image/avif avif;
image/webp webp;
image/heic heic heif;
}
3. Is It a CORS Error?
If the image is on a different domain (CDN, S3), CORS may block it:
Access to image at 'https://cdn.example.com/photo.jpg'
from origin 'https://your-site.com' has been blocked by CORS policy
Fix (S3 bucket CORS):
[{
"AllowedHeaders": ["*"],
"AllowedMethods": ["GET"],
"AllowedOrigins": ["https://your-site.com"],
"ExposeHeaders": ["Content-Length", "Content-Type"]
}]
Fix (Nginx):
location /images/ {
add_header Access-Control-Allow-Origin "*";
}
4. Is the Image Actually Corrupted?
Download the served image and compare:
# Download the image as served
curl -o downloaded.jpg https://your-site.com/images/photo.jpg
# Check if it's valid
file downloaded.jpg # Should say "JPEG image data"
# NOT "HTML document" or "ASCII text"
# Check first bytes
xxd downloaded.jpg | head -1
# JPEG starts with: ff d8 ff
# PNG starts with: 8950 4e47
If file says "HTML document", your server is returning an error page instead of the image.
Test with known-good images: sample JPG · sample PNG · sample WebP · sample AVIF
5. Content Security Policy Blocking?
Refused to load the image because it violates the Content Security Policy: img-src
Fix: Add the image domain to your CSP header:
Content-Security-Policy: img-src 'self' https://cdn.example.com;
6. Is the Format Supported?
| Format | Chrome | Firefox | Safari | Edge | |--------|--------|---------|--------|------| | JPEG | ✅ | ✅ | ✅ | ✅ | | PNG | ✅ | ✅ | ✅ | ✅ | | WebP | ✅ | ✅ | ✅ 14+ | ✅ | | AVIF | ✅ 85+ | ✅ 93+ | ✅ 16+ | ✅ 121+ | | HEIC | ❌ | ❌ | ✅ | ❌ |
Fix: Use <picture> with fallbacks:
<picture>
<source srcset="photo.avif" type="image/avif">
<source srcset="photo.webp" type="image/webp">
<img src="photo.jpg" alt="Description">
</picture>
7. Lazy Loading Issues?
<!-- Image outside viewport may not load -->
<img src="photo.jpg" loading="lazy" alt="...">
<!-- Fix: eager loading for above-the-fold images -->
<img src="hero.jpg" loading="eager" alt="..." fetchpriority="high">
Test with Sample Images
| Test | File | What to Verify | |------|------|---------------| | Valid JPEG | sample-500kb.jpg | Basic display | | Transparent PNG | transparent-logo.png | Alpha channel | | WebP format | sample-100kb.webp | Modern format support | | AVIF format | sample-100kb.avif | Next-gen format | | Corrupted JPEG | sample-corrupt.jpg | Error handling | | Wrong extension | sample-jpg-as-pdf.pdf | MIME detection |
Use our Image Formats Cheat Sheet for complete format reference.