自社のストレージ + CDN を使用
API Velocity / Momentum プランで自社のオブジェクトストレージと CDN を接続する方法。プロバイダー別のセットアップ、最小権限、接続チェック、モード切り替え時の動作を説明します。
対象プラン: API の Velocity および Momentum プラン。Ignite(CDN なし)、開発者サンドボックス(サンドボックスのファイルは一時的)、WordPress プラン(smallPict のマネージド CDN を使用)では利用できません。
概要: BYO モードでは、smallPict は元画像と最適化した画像を お客様の バケットに書き込み、お客様の CDN がそれを配信します。ストレージと CDN は常にセットで接続されます。どちらか一方だけを持ち込むことはできません。
BYO とは
Velocity と Momentum には 2 つの配信モードがあります。ダッシュボード → CDN とストレージ でいずれかを選択します。
- マネージド(既定): smallPict が元画像を自社の暗号化ストレージに保管し(90 日後に長期アーカイブへ移動)、最適化した画像を
cdn.smallpict.appから配信します。設定は不要です。 - 自社のストレージ + CDN を使用(BYO): お客様自身のアカウントにある S3 互換バケットと、お客様自身のアカウントにある CDN を接続します。新しいジョブごとに元画像と最適化ファイルがお客様のバケットに書き込まれ、API はお客様の CDN ドメイン上の URL を返します。
セットで接続するルールがあるのは、smallPict が配信の両面、つまりファイルの書き込みとキャッシュからの古いコピーの削除の両方を行える必要があるためです。CDN のないバケットではキャッシュのパージ先がなく、バケットのない CDN では配信するものがありません。
マネージドと BYO の比較
| マネージド(既定) | 自社のストレージ + CDN を使用 | |
|---|---|---|
| 元画像 | smallPict の暗号化ストレージ | お客様のバケット内の originals/<job_id>/<file> |
| 最適化ファイル | cdn.smallpict.app から配信 | お客様のバケット内の optimized/<job_id>.<ext>。お客様の CDN ドメインから配信 |
| ストレージ容量 | Velocity 50 GB、Momentum 100 GB | カウントされません。料金はストレージプロバイダーにお支払いください。 |
| CDN 帯域幅 | Velocity 30 GB/月、Momentum 200 GB/月 | カウントされません。料金は CDN プロバイダーにお支払いください。 |
| 変換 | プランの使用量にカウント | プランの使用量にカウント |
| smallPict が保持するコピー | あり(アカウントが有効な間) | なし。処理用の一時アップロードのみで、24 時間以内に削除されます。 |
| キャッシュパージ | 自動 | 自動(お客様の CDN の API 経由) |
| CDN のパフォーマンス、ドメイン、費用 | smallPict | お客様。smallPict はお客様の CDN のパフォーマンス、ドメイン、請求について責任を負いません。 |
対応プロバイダー
ストレージ(現在利用可能): Amazon S3、Cloudflare R2、Google Cloud Storage(S3 相互運用)、Alibaba Cloud OSS、Tencent Cloud COS、DigitalOcean Spaces、SumoPod Storage、MinIO、およびカスタムエンドポイント経由のその他の S3 互換サービス(Hetzner、Vultr、Wasabi など)。
CDN(現在利用可能): Cloudflare と Amazon CloudFront。
予定: Azure Blob Storage とその他の CDN。クラウドプロバイダー ページで早期アクセスをリクエストできます。
始める前に
- API Velocity または Momentum を利用中で、アカウント管理者としてサインインしていること。
- バケットと、そのバケットのファイルを HTTPS で配信する CDN があること。
- ストレージのエンドポイントにインターネットから HTTPS で到達できること。プライベート、内部、平文 HTTP のエンドポイントは拒否されます。
- smallPict 用に権限を絞ったキーを作成済みであること(後述の 最小権限 を参照)。アカウントのルートキーや管理者キーは使用しないでください。
次に ダッシュボード → CDN とストレージ を開き、自社のストレージ + CDN を使用 を選択して両方の項目を入力し、ストレージ + CDN を保存 を選択します。smallPict は接続チェック(後述)を実行し、合格した場合にのみ BYO に切り替えます。
セットアップガイド
A. Amazon S3 + Amazon CloudFront
- バケットを作成 します。リージョンは任意で、例えば
ap-southeast-1にmy-imagesを作成します。Block Public Access はオンのままにしてください。 - バケットをオリジンとする CloudFront ディストリビューションを作成 します。Origin access control (OAC) を使用して、バケットを非公開のまま CloudFront が読み取れるようにし、CloudFront が提示するバケットポリシーを適用します。
- 任意: 独自ドメイン。 ディストリビューションに代替ドメイン名(例:
images.example.com)と証明書を追加し、DNS レコードをディストリビューションに向けます。 - 次のポリシーで ストレージ用の IAM ユーザー(またはロール)を作成 します。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:PutObject", "s3:GetObject", "s3:DeleteObject" ], "Resource": "arn:aws:s3:::my-images/*" } ]}- 次のポリシーで CloudFront 用の IAM ユーザーを作成 します(同じユーザーでも構いません)。
cloudfront:GetDistributionは任意です。これがあると、CDN ドメインを空欄にした場合に smallPict がディストリビューションのドメインを検出できます。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "cloudfront:CreateInvalidation", "cloudfront:GetDistribution" ], "Resource": "arn:aws:cloudfront::123456789012:distribution/E2QWRUHAPOMQZL" } ]}- ダッシュボードで:
- ストレージプロバイダー Amazon S3、バケット
my-images、リージョンap-southeast-1、エンドポイントは空欄(リージョンの標準エンドポイントを使用)、そしてストレージ用のアクセスキー ID とシークレットを入力します。 - CDN Amazon CloudFront、Distribution ID(例:
E2QWRUHAPOMQZL)、CloudFront 用のアクセスキー ID とシークレット、任意で CDN ドメイン(https://images.example.com)を入力します。ドメインを空欄にすると、ディストリビューション自身の*.cloudfront.netドメインが使用されます。
- ストレージプロバイダー Amazon S3、バケット
B. Cloudflare R2 + Cloudflare
- R2 バケットを作成 します(例:
my-images)。 - 同じ Cloudflare アカウント内のゾーンで、バケットに カスタムドメインを接続 します(R2 → 対象バケット → Settings → Custom Domains)。例:
images.example.com。このドメインへのリクエストは Cloudflare のキャッシュを経由します。本番環境ではr2.devの開発用 URL を使用しないでください。 - このバケットのみに範囲を限定した Object Read & Write 権限で R2 API トークンを作成 します(R2 → Manage API tokens)。表示される Access Key ID と Secret Access Key をコピーします。
- ゾーンに対する Zone → Cache Purge → Purge 権限で Cloudflare API トークンを作成 します(My Profile → API Tokens)。Zone → Zone → Read は任意です。
- ダッシュボードで:
- ストレージプロバイダー Cloudflare R2、バケット
my-images、エンドポイントhttps://<account_id>.r2.cloudflarestorage.com(バケットの S3 API 詳細に表示)、リージョンauto、そして R2 の Access Key ID と Secret Access Key を入力します。 - CDN Cloudflare、Zone ID(ドメインの Overview ページ、API セクション)、API トークン、CDN ドメイン
https://images.example.comを入力します。
- ストレージプロバイダー Cloudflare R2、バケット
C. Google Cloud Storage + Cloudflare
Google Cloud Storage は、HMAC キーを使った S3 互換の XML API 経由で接続します。
- バケットを作成 します(例:
my-images)。 - サービスアカウントを作成 し、このバケットに対してのみ Storage Object User(
roles/storage.objectUser)ロールを付与します。 - サービスアカウント用の HMAC キーを作成 します: Cloud Storage → Settings → Interoperability → Create a key for a service account。アクセス ID とシークレットをコピーします。
- 最適化ファイルを CDN から読み取れるようにします。 Cloudflare は Cloud Storage から HTTPS でファイルを取得するため、
optimized/配下のオブジェクトは公開読み取り可能である必要があります。均一なバケットレベルのアクセスでは、allUsersに Storage Object Viewer ロールを付与するとバケット全体(originals/を含む)が読み取り可能になります。元画像を非公開にする必要がある場合は、前段に認証レイヤー(例: バケットへのリクエストに署名する Cloudflare Worker)を配置してください。 - プロキシ有効の Cloudflare DNS レコードを Cloud Storage に向けます(例:
images.example.com)。バケット名をホスト名と同じにしてc.storage.googleapis.comへのプロキシ有効の CNAME を使用するか、パスの先頭にバケット名を付けてstorage.googleapis.comにリクエストを送る Cloudflare Origin Rule を使用します。 - ゾーンに対する Zone → Cache Purge → Purge 権限で Cloudflare API トークンを作成 します(Zone → Zone → Read は任意)。
- ダッシュボードで: ストレージプロバイダー Google Cloud Storage、バケット、エンドポイント
https://storage.googleapis.com、リージョンauto、HMAC のアクセス ID とシークレット。CDN Cloudflare、Zone ID、API トークン、CDN ドメインhttps://images.example.com。
D. MinIO または任意の S3 互換ストレージ + Cloudflare
このセクションは、MinIO および Hetzner、Wasabi、Vultr、SumoPod、Alibaba Cloud OSS、Tencent Cloud COS、DigitalOcean Spaces などの S3 互換サービスを対象としています。
| プロバイダー | ダッシュボードでの選択 | エンドポイント | 注記 |
|---|---|---|---|
| MinIO | MinIO | お客様のサーバー(例: https://minio.example.com) | パススタイルのリクエストをオン。サーバーにはインターネットから HTTPS で到達できる必要があります。 |
| Hetzner Object Storage | カスタム S3 互換 | https://<location>.your-objectstorage.com | |
| Wasabi | カスタム S3 互換 | https://s3.<region>.wasabisys.com | |
| Vultr Object Storage | カスタム S3 互換 | https://<region>.vultrobjects.com | |
| SumoPod Storage | SumoPod Storage | SumoPod のストレージダッシュボードで確認 | パススタイルは既定でオン。 |
| Alibaba Cloud OSS | Alibaba Cloud OSS | https://oss-<region>.aliyuncs.com | 仮想ホスト形式のみ。 |
| Tencent Cloud COS | Tencent Cloud COS | https://cos.<region>.myqcloud.com | バケット名には APPID が含まれます(例: my-images-1250000000)。 |
| DigitalOcean Spaces | DigitalOcean Spaces | https://<region>.digitaloceanspaces.com |
- バケットを作成 し、そのバケットに限定してオブジェクトの put、get、delete ができるキーを作成します(後述の 最小権限 を参照)。
optimized/を CDN から読み取れるようにします。 例えば、optimized/*に対してのみ匿名のs3:GetObjectを許可するバケットポリシーを使用します。MinIO の場合:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": [ "*" ] }, "Action": [ "s3:GetObject" ], "Resource": [ "arn:aws:s3:::my-images/optimized/*" ] } ]}- バケットの前段に Cloudflare を配置します: バケットの公開ホストを指す、
images.example.com用のプロキシ有効の DNS レコードを作成します。プロバイダーがリクエストに自社のホスト名を必要とする場合は、Host ヘッダーを書き換える(パススタイルのホストではパスにバケット名を追加する)Cloudflare Origin Rule を追加します。 - ゾーンに対する Zone → Cache Purge → Purge 権限で Cloudflare API トークンを作成 します(Zone → Zone → Read は任意)。
- ダッシュボードで: 表からプロバイダーを選び、バケット、エンドポイント、リージョン(プロバイダーにリージョンがない場合は
auto)、キーを入力します。表で指定されている場合はパススタイルのリクエストをオンにします。続いて CDN Cloudflare、Zone ID、API トークン、CDN ドメインを入力します。
最小権限
smallPict には、1 つのバケットに対して必要な操作だけができるキーを渡してください。
| プロバイダー | 権限 |
|---|---|
| Amazon S3 | arn:aws:s3:::<bucket>/* に対する s3:PutObject、s3:GetObject、s3:DeleteObject |
| Amazon CloudFront | ディストリビューションに対する cloudfront:CreateInvalidation。ドメインを検出できるよう cloudfront:GetDistribution は任意 |
| Cloudflare R2 | バケットに範囲を限定した Object Read & Write の R2 API トークン |
| Cloudflare (CDN) | ゾーンに対する Zone → Cache Purge → Purge の API トークン。Zone → Zone → Read は任意 |
| Google Cloud Storage | バケットに対する roles/storage.objectUser を持つサービスアカウントの HMAC キー |
| Alibaba Cloud OSS | バケットに対する oss:PutObject、oss:GetObject、oss:DeleteObject を持つ RAM ユーザー |
| Tencent Cloud COS | バケットに対する cos:PutObject、cos:GetObject、cos:DeleteObject を持つ CAM サブユーザー(バケット名には APPID が含まれます) |
| DigitalOcean Spaces | バケットに限定した読み取り・書き込み・削除権限を持つ Spaces アクセスキー |
| MinIO / カスタム S3 互換 | <bucket>/* に対する put、get、delete。エンドポイントは公開 HTTPS である必要があります |
接続チェック
保存するたびに(および 再チェック を選択したときに)、smallPict は使用前に接続全体をチェックします。
- ストレージ: バケット内の
.smallpict-probe/に小さなテストファイルを書き込み、読み戻してから削除します。 - Cloudflare: ゾーンに単一 URL のテストパージを 1 回送信します。
- CloudFront:
/.smallpict-probe/...パスのテスト用無効化を 1 回作成します。これはその月の CloudFront 無効化パス数にカウントされます。
いずれかのステップが失敗した場合、何も切り替わりません。ダッシュボードには、バケット名や API トークンなど、該当する項目の横に理由が表示されます。接続チェックは 1 分あたり 5 回までに制限されています。
CDN からのバケットアクセス
optimized/は CDN から読み取れる必要があります。そのプレフィックスを公開読み取りにするか、非公開バケットへの CDN オリジンアクセス(CloudFront の Origin access control、または R2 のカスタムドメイン)を使用します。originals/は非公開のままで構いません。CDN がこれを必要とすることはありません。- CORS が必要なのは、ブラウザが JavaScript からクロスオリジンで画像を取得する場合(例:
fetch()や canvas)のみです。通常の<img>タグでは不要です。必要な場合は、サイトのオリジンからのGETとHEADを許可してください。
オブジェクトの配置と URL
| 種類 | バケット内のキー | URL |
|---|---|---|
| 元画像 | originals/<job_id>/<file> | 配信されません |
| 最適化ファイル | optimized/<job_id>.<ext> | <cdn_domain>/optimized/<job_id>.<ext> |
CDN ドメインにはパスのプレフィックスを含めることができます(例: https://example.com/images)。その場合、URL は https://example.com/images/optimized/<job_id>.<ext> になります。
キャッシュヘッダー
最適化ファイルは Cache-Control: public, max-age=31536000, immutable を付けて書き込まれます。ジョブごとに一意のキーが割り当てられるため、新しい結果には必ず新しい URL が付き、パージが必要になることはほとんどありません。CDN のキャッシュルールでこのヘッダーをより短い時間で上書きしないようにしてください。
キャッシュパージ
- API 経由のパージリクエスト(
POST /v1/purge)は、バッチにまとめて お客様の CDN に送られます。 - Cloudflare では、ファイルは URL 単位でパージされます。「Purge all」でパージされるのはお客様の配信ホストとパスプレフィックスのみで、ゾーン全体ではありません。
- CloudFront では、ファイルは無効化によってパージされます。「Purge all」は
/*の無効化を作成します。CloudFront の月間無料枠を超える無効化パスは、AWS からお客様のアカウントに請求されます。
モードの切り替え
モードを切り替えても 既存のファイルは移動しません。配信済みのファイルは現在の URL のままで、新しいジョブだけが新しいモードを使用します。
- マネージド → BYO: 新しいジョブはお客様のバケットに保存され、お客様の CDN から配信されます。すでに
cdn.smallpict.appにあるファイルはそのまま残ります。 - BYO → マネージド: 新しいジョブは再びマネージドストレージと CDN を使用します。BYO の設定とキーは未使用のまま保存されるため、接続を解除 を選択するまでは、再入力せずに元に戻すことができます。
- 接続を解除: 保存された BYO の設定とキーを削除し、マネージドに戻します。バケット内のファイルには一切触れません。
問題が発生した場合
- smallPict がバケットに書き込めない場合は再試行し、それでも失敗すると、API レスポンスで確認できる理由とともに ジョブを失敗 させます。
- ダッシュボードには、直近のエラーと発生日時が 再チェック ボタンとともに表示されます。
- メールは失敗したジョブごとではなく、インシデントごとに 1 通 届きます。
- smallPict が マネージドストレージにフォールバックすることはありません。お客様が選んでいない場所にファイルが保存されることはありません。
セキュリティ
- キーは 暗号化されたシークレットストア に保管されます。再表示されることはなく、API から返されることも、ログに書き込まれることもありません。
- キーをローテーションするには、新しいキーを入力して保存します。接続チェックは新しいキーで実行されます。保存済みのキーを維持するには、キーの欄を空欄のままにします。
- ストレージと CDN のエンドポイントは HTTPS を使用する必要があります。プライベート、ループバック、リンクローカル、その他の内部アドレスに解決されるエンドポイントは拒否されます。
- smallPict には個別の、権限を絞ったキーを使用し、BYO の利用をやめた場合はプロバイダーのコンソールでキーを無効化してください。