💥

Cloudflareで静的アセットをS3 Originで配布したい

2022/05/16に公開

JSとかCSS、画像などの静的ファイルはS3に向けて、それ以外はアプリケーションサーバーにルーティングしたいというよくあるケース。CloudFrontとかFastlyではよくやるパターンなのでCloudflareでもサクっとできるだろう思ってたけど意外と難しかったのでメモっておく。

まず前提として、Cloudflareではパスベースのルーティングがエンタープライズプランでしか使えない。
https://support.cloudflare.com/hc/en-us/articles/206190798-Using-Resolve-Override-in-Page-Rules

なので、example.com/_next/*はS3に、それ以外のパスはアプリケーションサーバーにルーティングするみたいなことがエンタープライズプラン以外では素直には設定できない。せめてProプランくらいでできるようになると嬉しいんだけどまあできないものは仕方ない。

追記: Load Balancingの機能を使うと実現できそうというのをCloudflareの人に教えてもらった。試してないけど、月$5からなので現実的な選択肢になるかも。

いくつか方法はありそうだけど、

  • サブドメインを分けてS3に向ける
  • Cloudflare Workersを使う

のどちらかがよさそう。

サブドメインを分けてS3に向ける

パスベースでなく、assets.example.comみたいなサブドメインをS3にCNAMEで向ける方法。これであればフリープランからいける。CloudflareのCNAMEはCNAMEといいつつ、CloudflareのCDNを通してターゲットにProxyするというものなので、エッジでのキャッシュもちゃんと効く。

注意しないといけないのは、CNAMEでS3を参照する場合、バケット名をドメイン名と合わせる必要があるということ。

https://docs.aws.amazon.com/AmazonS3/latest/userguide/VirtualHosting.html

この場合バケット名をassets.example.comにしてStatic Website Hostingを有効にしておく。

なお、先述したようにCloudflareのCNAMEは実質Proxyのようなものなので、CloudflareのPage RuleでHostヘッダを${buekct}.s3.${region}.amazonaws.comのような値に書き換えてやればバケット名はなんでもよくなるけど、CloudflareでHostヘッダを上書きするのはこれまたエンタープライズプライのみなので諦めた。

https://support.cloudflare.com/hc/en-us/articles/206652947-Using-Page-Rules-to-rewrite-Host-Headers

Cloudflare Workersを使う

基本的には上記のサブドメインの方法でいいと思うけど、すでにあるバケットを使いたくて名前をドメイン名に合わせたものに変えたくないとか、サブドメインじゃなくて絶対にパスベースでルーティングしたいみたいなケースではCloudflare Workersを使うとよさそう。

WorkerのコードはただProxyするだけのシンプルなもの。

const endpoint =
  "https://${bucket}.s3.ap-northeast-1.amazonaws.com";

export default {
  async fetch(request: Request): Promise<Response> {
    const { url } = request;
    const { pathname } = new URL(url);
    const newUrl = `${endpoint}${pathname}`;
    const newRequest = new Request(newUrl, {
      body: request.body,
      headers: request.headers,
      method: request.method,
      redirect: request.redirect,
    });
    return fetch(newRequest);
  },
};

そしてexample.com/static/* のようなパスをこのWorkerに向ける。これでキャッシュもしてくれるので便利。

$ curl -s -I https://s3-assets-test.hokaccha.dev/static/app.js | grep cache-status
cf-cache-status: HIT

単にProxyするだけでなく細かい制御もできるのでそういうのがやりたいケースでもWorkerはよさそう。ただしWorkerはフリープランだと10万リクエスト/dayというかなり多めとはいえ上限があるのでリクエスト数によっては課金が必要そう。

Workersを使うならそもそもS3じゃなくてCloudflare R2を使うというのもありそうだけど、それはまた別途検証してみようと思う。

Discussion