😊

Cloudflare Pages上にJamstack構成で作った趣味ブログをデプロイしてみた感想やtipsについて

2022/03/10に公開
3

Jamstack構成で趣味ブログを作った

趣味のロードバイクに関する話題を扱うブログをCloudflare Pagesで公開しました。作ったばっかりで記事が少ないですし、改善したい点もたくさんありますが、ひとまず公開まではこぎつけたため共有です。
https://ebifran-roadbike-blog.com/

このサイトは以下で作られていますが、今回はCloudflare Pagesの内容に絞って書いていきます。

  • Next.js(AppRouter)
  • tailwind css(Chakra UIから移行)
  • microCMS

Cloudflare Pages を選択した理由

SSGと相性のいいホスティングサービスといえばVercelとNetlifyがまず挙がると思います。この2つも実際に試しましたが、以下の理由で最終的にCloudflare Pagesを選択しました。

Vercelの無料プランでは広告掲載ができない

まず最初に検討したのはVercelでした。理由はNext.jsの開発元が運営しているということもあり、Next.jsとの相性が非常によさそうだったからです。実際に開発段階で使ってみて、特に設定周りを気にすることなくすんなりデプロイすることが出来てとても気に入りました。

ただ、せっかくブログを作るなら広告を貼ってお小遣い稼ぎをしたいと思っていました(強欲)
Vercelの無料プランについて記載のある記事をいろいろと参考にしたところ、現在の利用規約では個人利用であっても広告掲載が明確に禁じられているようでした。参考にさせていただいた記事はこちらです。(コメントでその後の経過が記載されています)
https://zenn.dev/lollipop_onl/articles/eoz-vercel-pricing-2020

有料プランであるProプランを契約することで商用利用が可能になりますが、その場合月に20ドルの費用がかかります。運営を始めたばかりのブログでは高すぎるという判断で採用を見送りました。

Netlifyはパフォーマンスがいまいち

次に検討したのはNetlifyです。独自ドメインを取得後は実際にNetlifyにデプロイして数日運用していました。

ただし、いろいろな記事で書かれているようにNetlifyは遅いです。Vercelと比較するとNetlifyはTTFBが4~5倍くらい遅かったです。せっかくSSGで作成するのに速度が出ないのは勿体ないなと感じてしまいました。

また、(自分の構成に問題があるのかもしれないですが)Netlify上でビルドが走るタイミングで500エラーが発生することが稀にありました。Vercelでは起きなかったので原因がよく分からず、ホスティングサービスを変えるなら今のうちと思って乗り換えることにしました。

Cloudflare Pagesは無料プランでも商用利用

そんなわけで次にたどり着いたのがCloudflare Pagesでした。

まずは自分の中で重要な、無料プランでも商用利用が可能なのかについて調べました。すると2021年の8月に以下のページで公式から利用可能という回答が出ています。
https://community.cloudflare.com/t/is-cloudflare-pages-workers-free-plan-free-for-commercial-use/291741

それに加えてCloudflare Pagesでは無料プランでも転送量の上限が無制限というおまけつきです。あまりにも魅力的すぎるので採用を決めました。

使ってみてどうだったか

前述の2サービスにあるような以下の機能はCloudflare Pagesにも備わっているため、特に困ることなく運用できています。

  • gitと連携し、ブランチにpushされたらデプロイ
  • デプロイ用のwebookを作成し、microCMS側で記事を更新したらデプロイ

Netlifyでネックだったパフォーマンス面についても改善されました。Vercelと同等くらいの速度が出ています。

SSRができないので注意 →可能(2022/3/23更新)

VercelやNetlifyはSSRに対応しているため、SSGとSSRのハイブリッド構成が可能ですが、Cloudflare Pagesは完全なSSGサイトとしてホストする必要があります(という認識ですが間違っていたら教えてください。。)

具体的には、Next.jsに備わっているプレビューモードのようなSSRが必要となる機能は使えなくなります。私の場合、記事のプレビューはlocalhostで立ち上げたNext.jsアプリでプレビューするという割り切り方をしたのと、これ以外でSSRを利用するページが無かったため問題にはなりませんでした。

コメントでいただきましたが、Cloudflare Workersと併用することでSSRが可能なようです。今のところSSRが必要な機能を実装する予定がないため詳しくは調べられていませんが、時間に余裕があれば調べて記事に使用と思います。

Cloudflare Pagesの設定の注意点やハマった点

前述の通り、当初NetlifyでホストしていたSSGとSSR(プレビュー機能のみ)のハイブリッド構成としていたNext.jsアプリをCloudflare Pagesに移行しました。それにあたってCloudflare Pagesの設定をデフォルトから変更する箇所があったり、エラーが起きた箇所があるので記載します。

完全なSSGサイトとしてホストするための設定

Netlifyでホストしていた時はpackage.jsonのscriptの設定は以下のようになっていました。

{
  "scripts": {
    "dev": "cross-env NODE_OPTIONS='--inspect' next dev",
    "build": "next build",
    "postbuild": "next-sitemap --config sitemap.config.js",
    "start": "next start",
    "lint": "next lint"
  },
}

本筋とは外れますが、以下の設定を入れています。

  • windows環境でnpm run devを動作させるための設定
  • サイトマップをnext-sitemapを使って作成するためにpostbuildの設定

この設定を以下のように変更しました。

{
  "scripts": {
    "dev": "cross-env NODE_OPTIONS='--inspect' next dev",
    "build": "next build",
    "postbuild": "next-sitemap --config sitemap.config.js",
    
    // ↓追加
    "preexport": "npm run build",
    "export": "next export",
    // ↑追加
    
    // startの設定は不要なので削除
    
    "lint": "next lint"
  },
}

その上で、Cloudflare Pagesのビルド設定は以下のように設定します。

ビルドコマンドにnpm run exportを設定することで以下の順にコマンドが実行されることになり、サイトマップの作成処理を走らせたうえでデプロイさせることができました。

  1. next buildが実行(preexportnpm run buildを指定しているため)
  2. next-sitemapのxml作成用コマンドが実行(postbuildで指定しているため)
  3. next exportが実行(Cloudflare Pagesのビルドコマンドで指定)

next-sitemapの作成処理でエラーが発生

前述の手順でnext-sitemapのコマンドが実行されるようになったものの、ビルド時にエラーが発生するようになりました。ログを見たところ以下の内容が出力されていました。

Error: Cannot find module 'node:fs/promises'
Require stack:
- /opt/buildhome/repo/node_modules/next-sitemap/dist/cjs/file/index.js
- /opt/buildhome/repo/node_modules/next-sitemap/dist/cjs/config/index.js
- /opt/buildhome/repo/node_modules/next-sitemap/dist/cjs/cli.js
- /opt/buildhome/repo/node_modules/next-sitemap/bin/next-sitemap
at Function.Module._resolveFilename (internal/modules/cjs/loader.js:966:15)
at Function.Module._load (internal/modules/cjs/loader.js:842:27)
at Module.require (internal/modules/cjs/loader.js:1026:19)
at require (internal/modules/cjs/helpers.js:72:18)
at Object.<anonymous> (/opt/buildhome/repo/node_modules/next-sitemap/dist/cjs/file/index.js:17:36)
at Module._compile (internal/modules/cjs/loader.js:1138:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1158:10)
at Module.load (internal/modules/cjs/loader.js:986:32)
at Function.Module._load (internal/modules/cjs/loader.js:879:14)
at Module.require (internal/modules/cjs/loader.js:1026:19) {
code: 'MODULE_NOT_FOUND',
requireStack: [
'/opt/buildhome/repo/node_modules/next-sitemap/dist/cjs/file/index.js',
'/opt/buildhome/repo/node_modules/next-sitemap/dist/cjs/config/index.js',
'/opt/buildhome/repo/node_modules/next-sitemap/dist/cjs/cli.js',
'/opt/buildhome/repo/node_modules/next-sitemap/bin/next-sitemap'
]
}

調べたところ、Cloudflare PagesのデフォルトのNode.jsのバージョンが12.18.0であることが原因のようです。環境変数NODE_VERSIONでバージョン16以上を指定することで解消しました。
https://developers.cloudflare.com/pages/platform/build-configuration/#language-support-and-tools

「pages.dev」のデフォルトドメインをnoindexにする

私のブログは独自ドメインを割り当てていますが、Cloudflare Pagesを初期設定したときに自動で割り当てられるpages.devドメインも有効のままになっています。複数のURLでアクセスできてしまうと、重複コンテンツとみなされてしまいSEO的に不利になるため対策が必要です。

調べたところ、このpages.devドメインを無効にして独自ドメインだけを有効にするという設定は存在しないようです(もし設定できる場合は教えてください)。仕方ないので、pages.devドメインはgoogleにindexされないように対策をします。

Cloudflare Pagesの場合、アプリのルート直下に_headersというテキストファイルを設置し、そのファイル内にURLのルールとレスポンスヘッダを記載することで、特定のURLにアクセスされた際に設定するレスポンスヘッダを制御することが可能です。
https://developers.cloudflare.com/pages/platform/headers/

具体的には、私のブログの場合は以下のように設定した_headersファイルをpublicフォルダの直下に設置しました。

https://ebifran-roadbike-blog.pages.dev/*
  X-Robots-Tag: noindex

こうすることで、pages.devドメインにアクセスされた場合はX-Robots-Tagnoindexを付与し、独自ドメインにアクセスされた場合は何も付与しない、という挙動にできました。

pages.devドメインの場合

独自ドメインの場合↓

まとめ

ホスティングサービス選びについて紆余曲折ありましたが、今のところはCloudflare Pagesを選択して正解だったと思っています。この記事が何かの参考になればうれしいです。

Next.jsとmicroCMSで開発してみて得た知見などは、また別の機会に記事にしようと思います。

Discussion

ebifranebifran

コメントありがとうございます。自分では詳細を調べられていないですが、いただいた内容を本文に反映しました。

にゃにゃにゃにゃにゃにゃ

私も一通りの開発を終え、2ヶ月前に無責任に書いたコメントの修正に戻って参りました。下書き画面プレビューの方法は、

  • React.useEffectを使い、microCMSのAPIを呼ぶ方法。NEXT_PUBLIC始まりの環境変数に、下書き取得用のAPIキーを持たせる必要がある。この方法はAPIキーを秘匿にできないので、権限を限定したAPIキーを用意する必要がある。
  • Cloudflare workersを用意する。workerは、contentIdとdraftKeyを受け取ると、microCMSのAPIを呼ぶようにする。フロント側でcontentId, draftKeyをworkerにわたし、返ってきたレスポンスをもとに画面を描画する。APIキーはCloudflare workersの環境変数に設定する。あまり詳しく調べてはいませんが、Cloudflare functionでもできそうです。
    以上、この記事を読んで助けられている私のような人の参考になれば。