こんにちは。YOUTRUSTでWebエンジニアをしている井上(YOUTRUST)です。
今回は、ユーザーがアップロードした画像が正しい向きで表示されない問題を解決した話を紹介します。Lambda関数での修正実装と、CloudFrontキャッシュの削除でコストを抑えた方法について詳しく説明します。
- YOUTRUSTで発生していた画像の向き問題
- 問題の調査プロセス
- 実装した解決策
- 新たな課題:CloudFrontの1年間キャッシュ
- 解決策:CloudFrontのキャッシュ削除
- ワイルドカードでコストを最小限に
- まとめ
YOUTRUSTで発生していた画像の向き問題
YOUTRUSTでは、ユーザーがアップロードした画像をS3に保存し、CloudFront経由で配信しています。
一部のユーザーがアップロードした画像が横向きや逆さまになってしまう不具合が発生していました。
例)

上記のように、本来は正しい向きで表示されるべきプロフィール画像が、90度回転した状態で表示されてしまっていました。
調査してみると、Exif情報のOrientationがnormal以外の値を持つ画像で問題が発生していることが判明しました。
Exif情報のOrientationとは
スマートフォンやデジタルカメラで写真を撮影する際、機器は内蔵されているジャイロセンサーや加速度センサーで撮影時の機器の向きを検知します。例えば:
- スマートフォンを縦に持って撮影
- スマートフォンを横向きで撮影
- カメラを逆さまに持って撮影
これらの情報は、Exif(Exchangeable Image File Format)のOrientation値として画像ファイルに記録されます。Orientation値は1〜8の数値で表現され、例えば: - 1: 正常な向き(normal) - 3: 180度回転が必要 - 6: 時計回りに90度回転が必要 - 8: 反時計回りに90度回転が必要
通常、画像を表示するソフトウェアはこのOrientation値を読み取って、自動的に正しい向きに回転して表示してくれます。
なぜこの問題が起きたのか
YOUTRUSTでは、アップロードされた画像をWebPフォーマットに変換して配信しています。WebPは優れた圧縮率と画質を両立できる画像フォーマットですが、WebPではExifのOrientation情報による自動回転補正がサポートされていないという特性があります。
つまり、スマートフォンで撮影した写真のように、Orientation情報に依存して正しい向きを表現している画像は、WebPに変換すると意図しない向きで表示されてしまうのです。
問題の調査プロセス
問題の原因を特定するために、以下の順序で調査を進めました:
S3にアップロードされている画像の確認
- 元画像は正しい向きで保存されているか?
画像処理のフローを追跡
- どのタイミングで向きがずれるのか?
Exif情報の理解
- Orientation情報とは何か、どのように機能するのか?
WebPフォーマットの仕様調査
- WebPにおけるExif情報の扱いを調査
- WebPではOrientation情報による自動補正が非対応であることが判明
実装した解決策
原因が特定できたので、画像処理を行うLambda関数(image-proxy)で以下の修正を実装しました:
- 画像のOrientation情報を読み取る
- Orientationに基づいて画像を正しい向きに回転・反転処理
- Orientation情報を「normal」に書き換える
- WebPに変換
実装の概要
修正内容は非常にシンプルで、画像処理パイプラインに.rotate()を追加しただけです:
例)
// 修正前 sharp(imageBuffer) .resize(width, height) .toBuffer(); // 修正後 sharp(imageBuffer) .rotate() // この1行を追加 .resize(width, height) .toBuffer();
rotate()メソッドは、画像のEXIFのOrientation情報を読み取り、それに基づいて自動的に画像を正しい向きに回転・反転させます。処理後はOrientation値が1(normal)にリセットされます。
この処理により、WebPに変換後も正しい向きで表示されるようになりました。
新たな課題:CloudFrontの1年間キャッシュ
修正を実装した結果、新規アップロード分は正しい向きで表示されるようになりました。しかし、既存の画像は依然として意図した向きで表示されていませんでした。
調査の結果、CloudFrontのキャッシュ保持期間が1年間に設定されていることが判明しました。
なぜ1年間のキャッシュ設定だったのか
YOUTRUSTでは、アセットファイルの配信において「ファイルの更新を想定しない」という設計を採用しています。
ファイルを更新する際は、ハッシュ値付きの新しいファイル名で配信します:
images/youtrust.png → images/youtrust-a89bd7e01.png
このイミュータブルな配信戦略により、キャッシュの無効化について考える必要がなくなり、長期間のキャッシュ設定が可能になります。通常時はこの設計で問題ありませんが、今回のような修正が必要な場合には課題となりました。
解決策:CloudFrontのキャッシュ削除
既存の画像を正しい向きで表示するためには、CloudFrontにキャッシュされている画像を削除する必要がありました。キャッシュの自然消滅を待つと最大1年間ユーザーに不便を強いることになるため、即座に対応できるInvalidation(キャッシュ削除)を実行することにしました。
ワイルドカードでコストを最小限に
CloudFrontのInvalidation(キャッシュ削除)は、パスごとに課金されます。無料枠は月1,000パスまでで、それを超えると$0.005/パスの料金が発生します。
YOUTRUSTの画像パス構造をイメージしやすくするため、簡略化した例を示します(実際の構造とは異なります):
/users/profile_images/001/original.jpg /users/profile_images/001/small.jpg /users/profile_images/002/original.png ...
実際には数千以上のディレクトリが存在し、その中にoriginalサイズやsmallサイズなどの画像が格納されています。
ユーザー数分の個別パスを指定すると、確実に無料枠を超えてしまいます。
そこで、ワイルドカードを使用することにしました:
/users/profile_images/*
この1つのパスで、全ユーザーのプロフィール画像キャッシュを削除できます。個別指定なら数千〜数万パスが必要なところを、たった1パスで済ませることができ、無料枠内で収めることができました。
実際の作業手順と画面
実際のCloudFrontマネジメントコンソールでの作業は以下の通りです:
- CloudFrontのマネジメントコンソールにアクセス
- 対象のディストリビューションを選択
- 「Invalidations」タブを開く
- 「Create Invalidation」をクリック
- オブジェクトパスの入力欄に
/users/profile_images/*を入力

画面にも記載されている通り、「オブジェクトパスを個別に追加するには、標準エディタを使用します」とありますが、ワイルドカード(*)を使用することで、一括での削除が可能です。
まとめ
今回の対応を通じて、以下の学びを得ることができました:
1. 画像フォーマットの特性を理解する重要性
WebPのようなフォーマットを採用する際は、その特性や制限事項を十分に理解しておく必要があります。特にExif情報の扱いなど、見落としがちな仕様に注意が必要です。
2. イミュータブルな設計の限界
ファイル更新を想定しないイミュータブルな設計は多くの利点がありますが、今回のような「既存の全ファイルに対する修正」が必要な場合には課題となることを学びました。
3. コスト最適化の視点
CloudFrontのワイルドカード機能を活用することで、大規模なキャッシュ削除でも無料枠内で実行できました。AWSの各サービスの料金体系を理解し、適切な機能を選択することが重要です。
4. 段階的な問題解決アプローチ
「なぜ画像が横向きになるのか」という表面的な問題から、WebPの仕様、キャッシュの課題と段階的に深掘りすることで、根本的な解決策にたどり着くことができました。
YOUTRUSTでは、ユーザーの皆様により良い体験を提供できるよう、技術的な改善を続けていきます。
最後まで読んでいただき、ありがとうございました!
YOUTRUSTでは、一緒にプロダクトをより良くしていく仲間を募集しています。
興味のある方は、ぜひカジュアル面談からどうぞ!