Open1

Vercel以外でNext.jsのgetStaticPropsを使うとCDNに長期間キャッシュされるかも

catnosecatnose

環境

  • Next.js on GAE(Google App Engine)の構成でこの問題を確認しています。
    • Next.js on Cloud Runでも同じ問題が発生すると思います。
    • Vercelにデプロイしている場合にはこの問題は発生しないと思います。
  • revalidateは指定していません(= ISRは使用していません)。GAEではISRがうまく動かないためです。

直面した問題

ふとgetStaticPropsを使っているページが本番環境で[1]CDNに長期間キャッシュされることが分かりました。レスポンスヘッダを見るとCache-Control: s-maxage=31536000, stale-while-revalidateという値が設定されています。CDNのキャッシュが削除されなかった場合、最大で31536000秒キャッシュされうるということです。

getStaticPropsを使っているページはビルド時点で静的ファイルとして出力されるため、SSR(getServerSideProps)よりもリクエスト時のサーバーの負荷を抑えることができます。

しかし、ヘッダにCache-Control: s-maxage=31536000が指定されてしまった場合、リリースのタイミングでCDNのキャッシュをクリアしないと古いバージョンのファイルが配信され続けてしまう可能性が出てきます。

この挙動の原因

この挙動の原因となっているのは、Next.jsの以下の部分です。

https://github.com/vercel/next.js/blob/1c1a4de0e2d38090fcf95ef0a6f6790006aaa124/packages/next/server/send-payload.ts#L25-L38

  ...
  } else if (typeof options.revalidate === 'number') {
    if (options.revalidate < 1) {
      throw new Error(
        `invariant: invalid Cache-Control duration provided: ${options.revalidate} < 1`
      )
    }

    res.setHeader(
      'Cache-Control',
      `s-maxage=${options.revalidate}, stale-while-revalidate`
    )
  } else if (options.revalidate === false) {
    res.setHeader('Cache-Control', `s-maxage=31536000, stale-while-revalidate`)
  }
  ...

getStaticPropsが使われている、かつ revalidateが指定されていない場合にはres.setHeader('Cache-Control', s-maxage=31536000, stale-while-revalidate)が呼び出されるようです。revalidate: 秒数を指定した場合、その秒数がs-maxageの値として設定されるようです。

関連するページ

以下のDiscussionでこの件について指摘されています。

https://github.com/vercel/next.js/discussions/28557

2022/01/26時点ではNext.jsのメンテナによる返信はありません。

個人的な感想

👆 のソースコードの部分だけでも条件分岐が数多くあり、「Vercelにデプロイすればzero configでいろいろいい感じになる」と「それ以外のNode.jsランタイムでも問題なく動かす」の両方を満たすようにフレームワークを設計するのはすごく大変なんだろうな…と改めて思いました

脚注
  1. ローカル開発環境ではCache-Controlの値が書き換えられるため問題なし ↩︎