私が歌川です

@utgwkk が書いている

utgw.netをCloudFront + AWS Lambda Function URLに移行した

移行しました。ペライチなのでそんなに難しいことはないだろうと思ったけど、細々とハマりどころがありました。

utgw.net

前回の移行はこちら。

blog.utgw.net

前提

モチベーション

今回の移行のモチベーションは、「おもしろ全部」が8割、Next.js製WebアプリケーションをLambda Function URLで配信する練習をそろそろやっておくか、という気持ちが2割です。

最近は、Next.js製のBFFサーバーをいかにして無限にスケールさせるか、への関心が高まっています。Lambda関数としてデプロイできればスケーラビリティが得られるんじゃないか、ということはよく思うけど試せていなかったので、自分の個人サイトというおもちゃで試してみるか、と思った次第です。

やり方 & ハマりポイント

先に結論から言うと、Next.js製アプリケーションをLambda Function URLにデプロイする方法については以下の記事でだいたい解説されているので、そっちをまず読むのがいいと思います。

serverless.co.jp

ここから先は細々としたハマりどころを紹介するコーナーになります。

CloudFront経由でLambda Function URLを実行すると {"Message": null} というエラーが返る

以下のStackOverflowと同じことが発生していました。

stackoverflow.com

確かオリジンリクエストポリシーを AllViewer ではなく AllViewerExceptHostHeader に設定することで直ったはず。HostヘッダがCloudFrontのものに上書きされた状態でリクエストがLambda Function URLに到達しておかしくなっていたのかな。

S3にあるはずのオブジェクトパス指定してリクエストを送っても404が返ってくる

これも先述したのと同じでオリジンリクエストポリシーを AllViewerExceptHostHeader に変えたら直った気がする。このあたりはコンソールをポチポチいじって試行錯誤しまくったので少し曖昧です。

静的ファイルがないとき404ではなく403が返る

CloudFrontからS3のオブジェクトを参照できるようにするポリシーを設定するとき、コンソールで指示されるJSONをコピペすると、存在しないオブジェクトを参照したときにHTTP 404ではなく403が返ってきます。

既にピンと来ている人もいると思いますが、GetObject APIを叩くクライアントがListBucket APIを叩く権限がないと404が返ってこない例のアレです。

docs.aws.amazon.com

これはAPIのドキュメントにも書いてあるし、いろいろ思うところはあるけど変えづらいのでしょう……ということでバケットに対してListObject APIを実行する権限を足してやりましょう。

If you have the s3:ListBucket permission on the bucket, Amazon S3 returns an HTTP status code 404 Not Found error.

If you don’t have the s3:ListBucket permission, Amazon S3 returns an HTTP status code 403 Access Denied error.

ACMの証明書はus-east-1リージョンで発行する

1敗

Next.jsのレスポンスキャッシュ

App Routerのキャッシュ機構についてまだ把握しきれていなくて、ドキュメントをつまみ食いしながら設定していました。

nextjs.org

ページコンポーネントと同じモジュールから revalidate: number という変数をexportすることでキャッシュの有効期限を設定できるようです。たとえば以下のような変数を定義すると、レスポンスが10分間キャッシュされます。

export const revalidate = 1200

この設定でNext.js 13.4.9を使っていたときは Cache-Control: s-maxage=1200, stale-while-revalidate というレスポンスヘッダが付与されており、stale-while-revalidateって秒数の指定が要るのでは? と思っていたけど、Next.js 15.0.3に上げたら Cache-Control: s-maxage=1200, stale-while-revalidate=31534800 に変わりました。

CloudFrontはstale-while-revalidateに対応しているので、これでキャッシュの期限が切れたときのレイテンシが隠蔽しやすくなりそう。

aws.amazon.com

Next.jsレイヤでのリダイレクトをキャッシュさせる

CloudFrontで3xx系のステータスコードのレスポンスをキャッシュさせるには、オリジンから Cache-Control レスポンスヘッダを付与してやる必要があります。

Next.jsには next.config.js にリダイレクト設定を書く機能がありますが、これだけだと Cache-Control レスポンスヘッダが付与されません。

nextjs.org

いったん以下のようなミドルウェアを書いてしのいでいます。

import { NextRequest, NextResponse } from "next/server";

export function middleware(request: NextRequest) {
  if (request.nextUrl.pathname.startsWith("/labs")) {
    const redirectUrl = new URL(
      request.nextUrl.pathname,
      "https://sugarheart.utgw.net"
    );
    return NextResponse.redirect(redirectUrl, {
      status: 308,
      headers: {
        "Cache-Control": "public, max-age=31536000, s-maxage=31536000",
      },
    });
  }
}

おわりに

aws-lambda-web-adapterや先人のブログ記事のおかげで、そんなに大きくハマることもなくNext.js製WebアプリケーションをCloudFront + Lambda Function URLの構成に移行できました。もっと複雑な構成になっている場合はいろいろあるかもしれません。

Lambda関数自体のデプロイはlambrollでやっていたのですが、こちらは全くハマることがなく使えました。便利ですね。

クラウド破産が見えてきたらまたお知らせします。