NetlifyをやめてCloudflare Pagesへ移行する【Hugo SSG】
Hugo で生成したコンテンツ(静的ウェブサイト)の公開用サーバーとして Netlify (無料プラン)を利用していましたが、若干アクセスが遅い気がしていたのと、Netlifyへのこだわりが強いわけでもなかったので、お引越し気分で Cloudflare Pages を試してみることにしました。
Netlify は本当に遅いのか
2020年8月時点の分析記事として、以下のブログが参考になります。
Netlifyが日本からだと遅い - id:anatooのブログ
約2年前ということで、今とは状況が違うかもしれません。そして、2021年5月時点、サポートによると東京ロケーションは High-Performance CDN と位置づけられているので(これは、無料枠ではないと解釈。調べていない)、2022年7月現在でも Netlify 無料枠を使う上では遅い解釈で正しいかもしれません。
Is there a list of where Netlify’s CDN pops are located? - Support - Netlify Support Forums
この記事では “Netlify が遅いから引っ越す” というよりも、”Cludflare Pages への好奇心” の方が移行の動機として大きいため、本件にはこれ以上言及しません。
引っ越しの前提条件
既に Hugo を用いてコンテンツは生成してあるので、生成先ディレクトリ(デフォルトでは public/
)を新たなホスティングへ配置し直すだけです。
この記事では2種類の接続方法を記載してます。
- GitHub と接続し、Commit が発生したら自動で Cloudflare Pages へデプロイする
- 公式CLI(wrangler cli)を用いて、ローカルのフォルダを Cloudflare Pages へデプロイする
今回は、Hugoの操作方法については記載していません。また、Cloudfareアカウントは作成済みであるものとします。
Cloudflar Pages プロジェクトの作り方
- GitHubと接続する過程で作成する
- ファイルをブラウザ経由でアップロードする
- wrangler cli を使って作成する
今回、最初にGitHubと接続をしたので、そのセットアップ過程でプロジェクトを作成しました。
GitHubへの接続
GitHubへの接続を選択する。アイコンにある通り、GitHubおよびGitLabと接続できるようです。
次に、ここでプロジェクト名を決定します。作成後も変更画面はありました。(試していません)
ここで作成したプロジェクト名が 後に公開されるURLになります。サイトは <your-project>.pages.dev
として公開されます。
サイト生成のフレームワークも多数選択できました。
フレームワークを選択した後は、ビルドコマンドを指定します。
- ビルドコマンド:
hugo --minify
- ビルド出力ディレクトリは Hugo 標準:
public
(変更なし)
必要に応じて、環境変数もここで追加します。マニュアルには記載が見当たりませんが、指定しないと古いバージョンが使われるとのことなので、ローカルで利用している hugo のバージョンと合わせておきます。バイナリは +extended
バージョンが利用されるようです。Hugo Modules を使っている場合も大丈夫とのこと。
(参考 Build configuration · Cloudflare Pages docs - Language support and tools)
そして設定が完了し、デプロイに移ります。
初デプロイが27秒で完了しました。上出来。
ドメイン(レコード)の切り替え
最後に、ドメインをアクティブにします。(デプロイの後にこの画面が勝手に出てきました)
前提として、自サイトでは既に Cloudflare をドメインのネームサーバとして指定していますので、Cloudflare Pages セットアップの流れで切り替え操作まで完了するフローになっています(と思われます)。
試していないので想像ですが、Cloudflare で管理していないドメインの場合は「このレコードに切り替えてね」といったアナウンス画面になるのではと思います。
ただ、設定するレコードは CNAME で *.pages.dev
向けになりますので、Zone Apex(頭にサブドメインを伴わないドメイン自体を指す)の場合は、一般的には CNAMEレコードが設定できません(RFC 1912 - Common DNS Operational and Configuration Errors)。その場合は結局、Cloudflare の管理下に移すことになるでしょう。
Netlify ではまさに、CNAEでNetlifyのロードバランサ用レコードを指す必要があったので、この時に Cloudflare 管理下に置いていました。
パフォーマンスを軽くベンチマーク
冒頭で「レスポンスは目的ではなかった」と言いつつも関心はありますので評価してみます。自サーバーではなくサービスプロバイダーの設備ですので、攻撃にならないよう控えめの数字で軽くベンチします。
前提:Apache Bench コマンドを使いますが、この実行環境の MacBook そのものが無線LAN接続なので、負荷評価用の環境としては非常にお粗末です。軽い参考・目安程度でご覧ください。
Netlifyだった場合のベンチ
ab -c 4 -n 100 https://*********
Server Software: Netlify
Server Hostname: *********
Server Port: 443
SSL/TLS Protocol: TLSv1.2,ECDHE-ECDSA-CHACHA20-POLY1305,256,256
Server Temp Key: ECDH X25519 253 bits
TLS Server Name: *********
Document Path: /
Document Length: 24332 bytes
Concurrency Level: 4
Time taken for tests: 12.878 seconds
Complete requests: 100
Failed requests: 0
Total transferred: 2466308 bytes
HTML transferred: 2433200 bytes
Requests per second: 7.77 [#/sec] (mean)
Time per request: 515.127 [ms] (mean)
Time per request: 128.782 [ms] (mean, across all concurrent requests)
Transfer rate: 187.02 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 229 275 54.7 244 432
Processing: 146 179 158.6 151 1723
Waiting: 77 123 140.7 86 1458
Total: 379 453 176.1 420 2117
Percentage of the requests served within a certain time (ms)
50% 420
66% 453
75% 474
80% 493
90% 528
95% 544
98% 583
99% 2117
100% 2117 (longest request)
# 2回目(1分後くらいに)
Concurrency Level: 4
Time taken for tests: 11.904 seconds
Complete requests: 100
Failed requests: 0
Total transferred: 2466400 bytes
HTML transferred: 2433200 bytes
Requests per second: 8.40 [#/sec] (mean)
Time per request: 476.148 [ms] (mean)
Time per request: 119.037 [ms] (mean, across all concurrent requests)
Transfer rate: 202.34 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 231 291 57.8 270 476
Processing: 146 169 41.9 151 353
Waiting: 76 107 40.4 83 256
Total: 378 460 81.3 437 812
Percentage of the requests served within a certain time (ms)
50% 437
66% 474
75% 495
80% 501
90% 563
95% 585
98% 785
99% 812
100% 812 (longest request)
Cloudflare Pagesのベンチ
❯ ab -c 4 -n 100 https://********/
Server Software: cloudflare
Server Hostname: ********
Server Port: 443
SSL/TLS Protocol: TLSv1.2,ECDHE-RSA-CHACHA20-POLY1305,2048,256
Server Temp Key: ECDH X25519 253 bits
TLS Server Name: ********
Document Path: /
Document Length: 23528 bytes
Concurrency Level: 4
Time taken for tests: 6.367 seconds
Complete requests: 100
Failed requests: 0
Total transferred: 2437464 bytes
HTML transferred: 2352800 bytes
Requests per second: 15.71 [#/sec] (mean)
Time per request: 254.672 [ms] (mean)
Time per request: 63.668 [ms] (mean, across all concurrent requests)
Transfer rate: 373.87 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 40 112 86.6 77 353
Processing: 36 123 123.8 64 628
Waiting: 31 96 105.9 55 516
Total: 82 234 143.5 158 871
Percentage of the requests served within a certain time (ms)
50% 158
66% 305
75% 336
80% 359
90% 401
95% 498
98% 681
99% 871
100% 871 (longest request)
# 2回目
Concurrency Level: 4
Time taken for tests: 14.858 seconds
Complete requests: 100
Failed requests: 0
Total transferred: 2437472 bytes
HTML transferred: 2352800 bytes
Requests per second: 6.73 [#/sec] (mean)
Time per request: 594.337 [ms] (mean)
Time per request: 148.584 [ms] (mean, across all concurrent requests)
Transfer rate: 160.20 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 37 453 488.3 91 1220
Processing: 34 91 79.8 64 396
Waiting: 33 68 42.2 56 228
Total: 80 545 491.8 254 1466
Percentage of the requests served within a certain time (ms)
50% 254
66% 1116
75% 1133
80% 1139
90% 1177
95% 1260
98% 1357
99% 1466
100% 1466 (longest request)
Connect の時間が大きく改善されました!と言いたいところですが、2回目のブレが多すぎました。
やはり有線LANでやらないとノイズが酷いですが、Netlify の2回平均よりも遥かに良好な値が Cloudflare Pages で一度確認できたので、とりあえず良しとします。
繰り返しますが、とても雑な評価なので参考程度にしてください。Netlifyを不当に悪評価とする意図はありません。
Cloudflare Pages CLI (wrangler) の利用
ローカルディレクトリをアップロードするための操作です。Wrangler そのものは、Cloudflare Workers のためのCLIのようです。
Wrangler is a command-line tool for building Cloudflare Workers
Get started · Cloudflare Workers docs
$ npm install -g wrangler
$ wrangler login
⛅️ wrangler 2.0.22
--------------------
Attempting to login via OAuth...
Successfully logged in.
Would you like to help improve Wrangler by sending usage metrics to Cloudflare? (y/n)
Your choice has been saved in the following file: ./.wrangler/config/metrics.json.
You can override the user level setting for a project in `wrangler.toml`:
- to disable sending metrics for a project: `send_metrics = false`
- to enable sending metrics for a project: `send_metrics = true`
プロジェクト一覧を確認できます。
$ wrangler pages project list
Retrieving cached values for account from node_modules/.cache/wrangler
┌──────────────┬───────────────────────┬──────────────┬────────────────┐
│ Project Name │ Project Domains │ Git Provider │ Last Modified │
├──────────────┼───────────────────────┼──────────────┼────────────────┤
│ *********** │ ***********.pages.dev │ Yes │ 17 minutes ago │
└──────────────┴───────────────────────┴──────────────┴────────────────┘
アップロードの際は、対象ディレクトリとプロジェクト名を指定して実行。
❯ CLOUDFLARE_ACCOUNT_ID=XXXXXXXXXXXXXXXXX npx wrangler pages publish public --project-name=***********
Retrieving cached values for account_id from node_modules/.cache/wrangler
🌍 Uploading... (287/287)
✨ Success! Uploaded 151 files (136 already uploaded) (3.87 sec)
✨ Deployment complete! Take a peek over at https://d000abcd.***********.pages.dev
たったの4秒で完了しました。Netlifyのときは数十秒かかるケースもあったので、地味に嬉しい。
CLIでアップロードした場合は、Web画面上では以下の通りシンプルな表示に。
Functions
Javascript または Typescript で用意できるみたいです。
Functions (beta) · Cloudflare Pages docs
今回の Hugo コンテンツでも独自のスクリプト(API)を一部で利用していますが、Netlify や Cloudflare Pages などのプロバイダーに依存しないよう別立てしています。おかげで今回、 Netlify への依存機能が無くて移行も1時間とかからなかったので、今後も別のプロバイダーを試す可能性がある場合は、疎結合にしておくのがよいかなと思います。
設定のメモ
最終的に設定した環境変数
本番環境へは HUGO_ENV: production
を設定しています。
***.pages.dev の検索エンジンインデックス回避
カスタムドメイン(独自ドメイン)を割り当てて使うケースも多いと思います。
カスタムドメインを割り当てたとしても、デフォルトで割り当てられる Cloudflare Pages のドメイン( ***.pages.dev
)は並列で利用できます。万一検索エンジンBOTがアクセスしてきてインデックスされると重複してしまいますので、以下の通り条件を指定して noindex ヘッダーを返すようにします。
_headers
ファイルを最上位(index.html等)の場所に配置し、以下の内容を記載するだけです。これにより、 X-Robots-Tag
を理解できるロボットのインデックスを回避できます。
https://<your-project-name>.pages.dev/*
X-Robots-Tag: noindex
robots メタタグの指定 | Google 検索セントラル | ドキュメント | Google Developers
ほかのやり方として、 rel=canonical
を設定しておくことでも問題ないでしょう。これならば、今後同等の問題が他のプロバイダーで発生しても問題を回避できますね。
重複した URL を正規タグに統合する | Google 検索セントラル | ドキュメント | Google Developers
まとめ
Netlify から Cloudflare Pages へ移行する経緯を記載しました。
もともと Cloudflare を DNS 環境として利用していてアカウントが整っていたというのもありますが、思い立ってから軽く調べてデプロイの完了まで1時間程度で完了したので、この手軽さは素晴らしいと思いました。
パフォーマンスの理由ももちろんですが、DNS と 静的サイト収容、そしてCDNを同一プロバイダーへまとめることができたので、管理画面が統合されたのも嬉しいです。複数の管理画面を行ったり来たりは面倒ですからね。
その他参考
- リダイレクトのやり方
- ヘッダーの設定方法