Next.js × Notion API環境における画像表示の課題と暫定対応(ISR編)
2025-05-25

はじめに
本サイトは、NotionをCMSにしたNext.jsのブログです。
このブログサイトを最近リリースしたのですが、「画像が本番環境で表示されない」という問題に直面したため、その原因と対応策を共有します。
今回は一旦の暫定対応として、ISR(Incremental Static Regeneration)を1時間間隔で再生成する方法を採用しました。その経緯と今後の課題も含めて、記載していきます。
事象
以下のような問題が本番環境で発生しました。
- ローカル環境では正常に表示される画像が、本番環境では表示されない
ローカル

本番

- DevToolsのネットワークログを見ると、画像のリクエストに対して 403 Forbidden エラーが返っている

実際の表示されない画像のリクエストURLを確認すると、以下のような一時的な署名付きURLになっていました。
https://prod-files-secure.s3.us-west-2.amazonaws.com/abc...xyz/sample.png?X-Amz-Expires=3600&X-Amz-Signature=...原因調査
Notionの画像について調べてみたところ、画像URLの仕組みが関係していることがわかりました。
- Notion API 経由で取得される画像URLは、S3の署名付きURLで提供されています
- URLには X-Amz-Expires=3600 などのパラメータが付与されており、有効期限は約1時間
- 有効期限が切れると、その画像URLは無効になり、403 Forbidden が返される
したがって、ISRやSSGで生成されたページに含まれる画像URLは、時間が経つとアクセス不能になるということがわかりました。
参考リンク:
検討した対策案(簡易比較)
対応策として、上記記事やAIを参考に検討しました。
- ISR(再生成間隔1時間)
- Notionに external.url を貼る
- GCSなどに自動アップロードし、そのURLを参照
- ビルド前に画像をDLして public/ に保存
- SSRで毎回URLを再取得
- ISRの再生成間隔を1時間にする(今回採用)
実際に取った対応: ISRの再生成を1時間に設定
今回は 最も手軽に導入できるという理由から、Next.js の ISR を利用し、再生成間隔を1時間(3600秒)に設定しました。
App Router 構成での設定例
// app/[slug]/page.tsx
export const revalidate = 3600; // 1時間この設定により、各ページは 1時間ごとに再生成され、Notionの画像URLもそのタイミングで更新されます。結果として、403の発生率はある程度抑えられます。
現在の状態と課題
この対応によって、画像が表示されるケースは増えました。
しかし、ISRの性質上、1時間以上アクセスがないページは再生成されず、次のアクセス時に403画像が出る可能性があります。
これは仕組み上どうしても避けられないため、あくまで暫定対応としようかなと思いました。
まとめ
- Notion の画像URLは 期限付きの署名付きURL
- SSGやISRで生成したページ内の画像URLは 時間経過で無効化される
- 一時対応として ISR の再生成間隔を 1時間 に設定したが、完全には安定しない
本質的な解決を目指すには、画像を外部ストレージに保存し直したり、external.url の活用など、永続URLを生成・管理する仕組みが必要です

