画像読み込みエラーのハンドリングベストプラクティス - フォールバックと UX 改善
画像読み込みエラーの種類と原因 - なぜ画像は表示されないのか
Web ページで画像が表示されない原因は多岐にわたります。適切なエラーハンドリングを実装するためには、まずエラーの種類を理解する必要があります。
主なエラー原因:
- 404 Not Found: 画像ファイルが存在しない。URL の typo、ファイルの削除、パスの変更が原因
- 403 Forbidden: アクセス権限がない。CDN の署名付き URL の期限切れ、CORS 設定の不備
- ネットワークエラー: 接続タイムアウト、DNS 解決失敗、オフライン状態
- デコードエラー: ファイルが破損している、拡張子と実際のフォーマットが不一致
- CSP (Content Security Policy) ブロック: セキュリティポリシーにより外部画像の読み込みが拒否される
- Mixed Content: HTTPS ページから HTTP の画像を読み込もうとして拒否される
エラーの影響: 画像エラーを放置すると、ブラウザはデフォルトの壊れた画像アイコン (破れた画像マーク) を表示します。これはユーザーに「サイトが壊れている」という印象を与え、信頼性を損ないます。特に EC サイトの商品画像やプロフィール画像が表示されない場合、コンバージョン率に直接影響します。
HTTP Archive の調査によると、Web ページの画像リクエストの約 3-5% がエラーを返しています。100 枚の画像があるページでは、3-5 枚が表示されない計算です。この数字は無視できるものではなく、適切なフォールバック戦略が必要です。
onerror イベントによる基本的なフォールバック実装
HTML の <img> 要素は、画像の読み込みに失敗すると error イベントを発火します。このイベントをハンドリングすることで、フォールバック画像への切り替えやプレースホルダーの表示が可能です。
インラインでのフォールバック:
<img src="product.jpg" onerror="this.src='/images/fallback.png'; this.onerror=null;" alt="商品画像">
this.onerror=null は重要です。フォールバック画像も読み込みに失敗した場合、無限ループを防止します。これがないと、フォールバック画像のエラーが再度 onerror を発火し、永遠にリクエストが繰り返されます。
JavaScript での実装:
document.querySelectorAll('img').forEach(img => { img.addEventListener('error', function() { this.src = '/images/fallback.svg'; this.classList.add('img-error'); this.removeEventListener('error', arguments.callee); });});
React での実装:
function ImageWithFallback({ src, fallback, alt }) { const [imgSrc, setImgSrc] = useState(src); const [hasError, setHasError] = useState(false); return ( <img src={hasError ? fallback : imgSrc} onError={() => { setHasError(true); }} alt={alt} /> );}
注意点: onerror は画像の HTTP レスポンスがエラーの場合だけでなく、デコードに失敗した場合にも発火します。また、src 属性が空文字列の場合は error イベントが発火しないブラウザもあるため、空の src は避けてください。
フォールバック画像の設計 - 用途別の最適なアプローチ
フォールバック画像は「画像が表示できない」ことをユーザーに伝えつつ、ページの見た目を大きく損なわないデザインが求められます。用途に応じた最適なフォールバックを設計します。
汎用フォールバック (SVG 推奨): サイト全体で使用する汎用的なフォールバック画像は、SVG で作成することを推奨します。SVG はどのサイズでもシャープに表示され、ファイルサイズも小さい (通常 1KB 未満) ため、追加のネットワークリクエストの負荷が最小限です。
<svg viewBox="0 0 400 300" xmlns="http://www.w3.org/2000/svg"> <rect fill="#f0f0f0" width="400" height="300"/> <text x="200" y="150" text-anchor="middle" fill="#999">Image</text></svg>
ユーザーアバター用フォールバック: プロフィール画像が読み込めない場合は、ユーザーのイニシャルを表示する CSS ベースのフォールバックが効果的です。背景色をユーザー名のハッシュから生成すれば、ユーザーごとに異なる色のアバターが表示されます。
商品画像用フォールバック: EC サイトでは「画像準備中」のプレースホルダーを表示し、商品情報 (名前、価格) は引き続き表示することで、ユーザーの購買行動を妨げません。カテゴリごとに異なるフォールバック画像 (衣類のシルエット、電子機器のアイコンなど) を用意すると、より親切です。
Data URI によるインラインフォールバック: 外部ファイルへのリクエストすら失敗する可能性がある場合 (オフライン環境など)、Base64 エンコードした SVG を Data URI として直接埋め込む方法が最も確実です。
const FALLBACK = 'data:image/svg+xml;base64,PHN2ZyB...';
CSS によるエラー状態のスタイリング - 壊れた画像を美しく隠す
CSS だけで画像エラー時の表示を改善する方法があります。JavaScript が無効な環境でも動作するため、プログレッシブエンハンスメントの観点から有効です。
壊れた画像アイコンを隠す: 画像の読み込みに失敗すると、ブラウザは alt テキストと壊れた画像アイコンを表示します。以下の CSS で、エラー時にカスタムスタイルを適用できます。
img { font-family: sans-serif; font-size: 0.875rem; color: #666; text-align: center; display: flex; align-items: center; justify-content: center;}
::before と ::after 疑似要素の活用: 画像が正常に読み込まれた場合、::before と ::after 疑似要素は表示されません。しかし、読み込みに失敗した場合はこれらの疑似要素が有効になります。この特性を利用して、エラー時のみカスタムコンテンツを表示できます。
img::before { content: ''; display: block; position: absolute; inset: 0; background: #f5f5f5;}img::after { content: attr(alt) ' (画像を読み込めませんでした)'; display: block; position: absolute; inset: 0; padding: 1rem; background: #f9f9f9; border: 1px dashed #ddd;}
注意点: 疑似要素による方法はブラウザ間で挙動が異なります。Chrome と Firefox では動作しますが、Safari では <img> の疑似要素がサポートされていない場合があります。クロスブラウザ対応が必要な場合は、JavaScript による onerror ハンドリングと併用してください。
object-fit との組み合わせ: object-fit: cover を使用している画像がエラーになった場合、フォールバック画像にも同じ object-fit が適用されます。フォールバック画像のアスペクト比が異なる場合に意図しないクロッピングが発生しないよう、フォールバック画像は正方形で作成することを推奨します。
リトライ戦略とプログレッシブローディング - 一時的なエラーへの対応
ネットワークの一時的な不安定さによる画像読み込みエラーは、リトライで解決できる場合があります。ただし、無制限のリトライはサーバーに負荷をかけるため、適切な制限と戦略が必要です。
指数バックオフによるリトライ:
function loadImageWithRetry(img, src, maxRetries = 3) { let retries = 0; img.onerror = () => { if (retries < maxRetries) { retries++; const delay = Math.pow(2, retries) * 1000; setTimeout(() => { img.src = src + '?retry=' + retries; }, delay); } else { img.src = '/images/fallback.svg'; img.onerror = null; } }; img.src = src;}
リトライ間隔は 2 秒、4 秒、8 秒と指数的に増加させます。クエリパラメータ (?retry=N) を付与することで、CDN のキャッシュされたエラーレスポンスを回避します。
Intersection Observer との組み合わせ: 遅延読み込みと組み合わせる場合、ビューポートに入ったタイミングで読み込みを開始し、エラー時にリトライする構成が効果的です。ユーザーがスクロールして画像が見える位置に来た時点でリトライが実行されるため、不要なリクエストを最小限に抑えられます。
Service Worker によるオフラインフォールバック: Service Worker を使えば、オフライン時に自動的にキャッシュ済みのフォールバック画像を返すことができます。ネットワークリクエストが失敗した場合に、事前にキャッシュしておいたプレースホルダー画像を返す実装が可能です。
リトライすべきでないケース: 404 や 403 エラーはリトライしても解決しません。fetch API で事前にステータスコードを確認し、4xx エラーの場合は即座にフォールバックに切り替える判断が効率的です。リトライは 5xx エラーやネットワークエラーに限定してください。
監視とエラーレポーティング - 画像エラーの可視化と改善
画像エラーを検知し、継続的に改善するための監視体制を構築します。エラーの発生状況を可視化することで、問題の早期発見と対応が可能になります。
エラーイベントの収集:
window.addEventListener('error', (event) => { if (event.target.tagName === 'IMG') { const errorData = { src: event.target.src, alt: event.target.alt, page: window.location.href, timestamp: Date.now() }; navigator.sendBeacon('/api/image-errors', JSON.stringify(errorData)); }}, true);
navigator.sendBeacon を使用することで、ページ遷移時にもデータが確実に送信されます。addEventListener の第 3 引数に true を指定してキャプチャフェーズでイベントを捕捉する点が重要です。
監視すべきメトリクス:
- 画像エラー率 (エラー数 / 総画像リクエスト数)
- エラーの多い URL パターン (特定のパスや CDN に集中していないか)
- エラーの時間帯分布 (特定の時間帯に集中する場合、CDN やオリジンの問題の可能性)
- ブラウザ・デバイス別のエラー率 (特定環境でのみ発生する問題の検出)
アラート設定: 画像エラー率が通常の 2 倍を超えた場合にアラートを発報する設定を推奨します。CDN の障害やデプロイミスによる大量エラーを早期に検知できます。
定期的な死活監視: 重要な画像 (ロゴ、ヒーロー画像、主要商品画像) に対して、定期的に HTTP リクエストを送信し、200 レスポンスが返ることを確認する外形監視を実装してください。Uptime Robot や AWS CloudWatch Synthetics などのサービスが利用可能です。