• 作成:

このサイトのホスティングを自宅サーバからCloudflare Pagesに移行させました

このサイトは2月末までは自宅サーバでホスティングしていました

もともとこのサイトはGitのログを見る限り2014-03-24に作ったらしいです。最初は単なるプロフィールと各SNSへのリンクを貼っていただけでした。

次第にHakyllを導入して日記を書くようにしたり、日付だけだと無理があるなと思ってまあ時刻だと被らないだろうと拡張していました。

当初は自宅サーバにHTMLを直接置いて、それをnginxだけで素朴に配信していました。一応早期から無料TLS証明書を取得してHTTPSで配信などは行っていました。

そこに、海外からDDoS攻撃してくるカメラをシャットダウンしてしまうのは不正アクセスなのか?自首してみたが返答がない!そして泥沼のDDoSへ - ncaq という問題が起きたので、 DDoSへの根本的な解決策にはなりませんが、とりあえずサイトをオンラインにし続けるために、 Cloudflare のキャッシュを通しておくことにしました。

昔から静的サイト配信サービスに移行したかった

情報セキュリティの3要素としてよく機密性、完全性、可用性が上げられますが、自宅サーバでの運用はどれも静的サイト配信サービスに劣っています。

  • 機密性: nginxやOpenSSLの脆弱性情報を個人で常に把握していかなければいけない、静的HTMLとは言えサーバに直接アクセスさせている
  • 完全性: nginxなどの設定を間違えると改竄されるリスクが比較的高い
  • 可用性: 自宅の停電とかサーバの再起動などで中断される

自宅のネットワーク回線の上りが非常に遅いため、自宅ネットワークの安定性とサイト自体の速度向上のことも考えて静的サイト配信サービスに移行したいと思っていました。

また自宅サーバの役割を減らして、最悪廃止したいという展望もあります。自宅サーバの管理が面倒くさくなってきたのと飽きたのと物理サーバに依存することを減らしたいと言うのが動機ですね。メールサーバもマネージドなレンタルサーバに移行する計画があります。

高校生の頃に始めた頃はnginxとか弄ってそれなりに学習に役立ちましたが、今となっては特に得るものもありません。

しかし移行したくないなという理由がありました。

GitHub PagesはHTMLをコミットしたくない

ブランチを専用のものに出来るとは言え静的サイトジェネレータが生成するHTMLはソースコードではなくビルドした成果物です。ビルドされたファイルをコミットしたくはありませんでした。

最近では小さいテキストファイルなどはそんなにlogやdiffが爆発しないし、むしろdiffを読みやすくて良いと思うようになりましたが、静的サイトジェネレータの生成物はあまりにも巨大です。

と思ってたんですが最近改善したらしいですね。 GitHub Pages のデプロイに Custom GitHub Actions Workflows を使用することで、ビルドの成果物の commit が不要になりました | DevelopersIO

以前のCloudflare Pagesはサーバでビルドしないといけなかった

ビルドは手元でやりたかったです。コミットに連動してビルドはやっていられません。

なぜかと言うと、 Build configuration · Cloudflare Pages docs によるとHaskell(GHC)はビルド環境に入っていないので、ビルドに非常に時間がかかる上に環境整備が大変そうだなあと思ったからです。

ところでなんでEmacsはシステムに入ってるんでしょうね? Emacs Lispでバッジ処理とか? org-modeの変換に使うのを目的にしてるとか?

またHakyllはページごとにMarkdownなどの変換を依存関係解析して並列処理で行ってくれるため、ページ数が多くてもそんなに時間がかからずビルドが完了するのですが、手元のRyzen 7950X(16コア32スレッド)ではサクサク終わっても、クラウドのビルド環境ではそうは行かないでしょう。この辺のコア数の情報はさっと見た感じ見つかりませんでしたが(2コアらしい?)。

もちろんHakyllにもインクリメンタルビルドの仕組みはあり、差分だけ更新することは可能なのですが、環境サポート外なのでちゃんと機能するのか疑問ですし、逆にキャッシュを外したい時に色々操作するのも面倒です。

そもそも仮にサポートされてたとしても、 1文字のtypoの修正にコミットするとかだるすぎるので、ローカルでしばらくwatchスクリプトを動かして検証したいです。

これが複数人がPRを送るシステムならば、それぞれ環境構築しなくてもプレビュー見れてサーバでのビルドは便利なのですが、個人でやるならローカルのビルドに勝るものはありません。

今のCloudflare PagesはWranglerで手元でシンプルにビルドできます

だいぶ前からこの機能は提供されていたので今更の話になりますが、 Wrangler コマンドラインツールを使うか、直接ファイルをアップロードすることでPagesにHTMLなどを直接アップロード可能になりました。これでビルド成果物をコミットする必要もありませんし、ビルドは手元で可能です。

また上記の通りCloudflareのキャッシュ機能を使うために既にCloudflareは使っていますし、ドメインレジストラもCloudflareなので色々と面倒がありません。

インストール

Pages -> プロジェクトを作成で開始です。自分の場合サーバでビルドはしないので Wrangler CLI を使用を選びます。

このページでは何故かグローバル環境にWranglerをインストールすることが推奨されているのですが、ドキュメントページではプロジェクトローカルにインストールすることが推奨されていました。

Install Wrangler locally Cloudflare recommends installing Wrangler locally into each of your projects. This allows you and your team to use the same Wrangler version, control Wrangler versions for each project, and rollback to an earlier version of Wrangler, if needed.

Install/Update Wrangler · Cloudflare Workers docs

当然そちらの方が良いと思ったので、

yarn add --dev wrangler

でインストールしました。

ログイン

yarn wrangler login でログインします。ブラウザが開かれるのでそれでログインするだけの簡単認証です。

てっきりプロジェクトに認証情報が保存されるとか、環境変数を.envrcでdirenvで設定するなどの方法が使われると思っていたのですが、マシン単位でログイン情報を管理するようですね。

そちらのほうがうっかりリポジトリに認証情報を保存してしまったとかの危険性が低そうで良さそうです。

ただCLOUDFLARE_ACCOUNT_IDは環境変数になるようです。まあこれはIDなので秘匿情報では無いはずですが、念の為direnvで隠しておくことにしました。

publishする

いきなりリポジトリ内部の設定ファイル生成も何もなしに、

wrangler pages publish <directory>

しろと言われて少し怯みました。

最初からlockかけたいサイトとかだと立ち上げどうするんでしょうね。最初は空の成果物をpublishするとかですかね? wrangler pages project create を使うとかですかね?(試してない) そもそもそういう機密を含むサイトにCloudflare Pagesは向いてなさそうというのは確かにそうなのですが。いやGitHubのチーム単位で制限できるから実は向いてたりもするのかもしれませんが。 Cloudflare Access + Pagesで自分のみアクセスできるようにする - notebook

publishする時にpromptで色々と設定を聞かれるのですね。

PagesなのでProject Nameにwww.ncaq.netと入力したら、

Project names can be 1-58 lowercase characters with dashes

とドットは使えないよと弾かれてしまいました。後述する通りプロジェクト名はカスタムドメインでないサイトやプレビューページなどのホスト名に使われるため、ネストしたホスト名にならないようにドットは使えないようですね。

なのでcabalでのnameと同じくwww-ncaq-netとすることにしました。余談なのですが、URLの順番とドメインの順番って逆なので、こういうDNSから離れた話になるとnet-ncaq-wwwみたいにトップレベルから始めたくなりませんか? Javaみたいな理論ですけど。

アップロードが完了するとURLhttps://www-ncaq-net.pages.dev/が表示されました。ちゃんと見れました。それでサイトが表示されたらWebコンソールからプロジェクトを弄るようですね。

やはりこういうのはTerraformなどで管理したほうが良いのではという気もしてきます。ちょっと前に調べたらCfnとかと比べて後からimportしてもそれなりのものが出来るらしいから後回しでも良さそうですが。 AWSと違ってWebコンソールが混沌としてたりしてないし、ドメイン登録料金以外で今の所お金払う予定が無いからあまり動機がないというのもあります。

WorkerとかD1とかをちゃんと試して何かを作るつもりになれば、ちゃんと管理するかもしれません。でもサーバレスな機能しかないし、 AWSにおけるServerlessみたいにwranglerだけでも割となんとかなってしまいそうなんですよね。自分はAWSは断然AWS CDK派閥で、 GitHubでコントビュートするぐらいですけど。

wrangler pages dev でローカルでテストすることも出来るようですね。別にwranglerに寄せる必然性も無いですが。 Cloudflare WorkerとかでCloudflareの機能使うなら便利なのかもしれません。(試してない) 後は後述する.html拡張子の問題も解決してくれるかも?(試してない)

Cloudflare Web Analytics

設定項目に、 Cloudflare Web Analytics | Cloudflare というものがあって、おっscriptを実行しないGoogle Analyticsが効かないようなアクセスも集計できるのかなと期待したのですが、そういうエッジでの測定は有料版限定のようです。無償版はGoogle Analyticsと大差ないクライアントJavaScriptで情報送るやつのようなので重複して有効にする必要は感じませんね。

カスタムドメイン

https://www-ncaq-net.pages.dev/だと不格好なので当然カスタムドメインを設定します。

素直にCNAMEを設定する方法のようです。元から全部Cloudflareで管理していれば既存のレコードを書き換えてくれます。

しかしdevの方にも当然ですがアクセスできてしまうんですよね。 Googleの方には適切に正規URLを提供すれば重複してインデックスされることは無いと思いますが、少し気になります。

そこで_headersファイルを配信ディレクトリに含んで、

https://:www-ncaq-net.pages.dev/*
  X-Robots-Tag: noindex

のように書けばプレビューページも含めてインデックスされなくなります。コロンによって幅広くマッチするようです。行儀の悪い検索エンジンはどうせIPアドレスも含めてインデックスしてくるでしょうし考えるだけ無駄ですね。

404処理について

デフォルトで何も設定してないと存在しないURLにアクセスしたら黙ってトップページにリダイレクトされてしまいますね。これはちょっと罠ですね。

ちょっと解決するのに苦戦したのですが、 /404.htmlをトップレベルに置く必要があるようです。 /404/index.htmlだとダメです。

というかこんなことを書いていて、

Route matching

If an HTML file is found with a matching path to the current route requested, Pages will serve it. Pages will also redirect HTML pages to their extension-less counterparts: for instance, /contact.html will be redirected to /contact, and /about/index.html will be redirected to /about/.

Serving Pages · Cloudflare Pages docs

デフォルトで.htmlを消してスラッシュにするそうです。私は元から拡張子をページとしてのURLに設定するのはクールではないと思っていたので、わざわざindex.htmlを作ってそれを参照させていたのでノーダメージでした。例えばHTMLからXHTMLに移行したら(XHTMLは可能性低いけど次世代プラットフォームはあり得る)URLが変わってしまうのはクールでは無いと思っているので。クールなURIは変わらない -- Style Guide for Online Hypertext

しかし最初からここで作る人は大丈夫ですが、他から移行する人はどうするんでしょうね? 実際諦めた人も居るみたいです。 VPSを解約してFirebase Hostingにブログを移した | おそらくはそれさえも平凡な日々

また404ページ自体に直接アクセスしてしまうと200になってしまうのは少し気になる所です。 Cloudflare PagesのFunctionsを使ってステータスコードを上書きするによるとFunctionsを使うと割と容易に変えられるらしいですが、 Googleに怒られるまでは放置でも良いかなと思っています。

キャッシュ

正直良く分かりませんでした。デプロイがあった時点でCloudflare Pages内部のデータは全部更新されるらしいことは分かりました。

ブラウザについてもETagがついていて初回アクセス時は、 cache-control: public, max-age=0, must-revalidateが付いているから未検証のまま使い回される危険性は低いのでしょうか。どうも確認リクエストを送っているのかよく見えない。私のネットワークタブの見方が見方が悪いのでしょうか…?

自宅サーバがオリジンだった時は画像や動画は長いキャッシュ保持にしてCloudflareの内部に長く留まるようにして多少高速にしたり、ネットワークの帯域を節約する必要がありましたが、始めからCloudflareの内部に入っているならばあまり気にする必要も無いでしょう。無料ですし。

タグでキャッシュを確認するのでユーザのネットワーク帯域もそんなに心配しなくて良いでしょう。

かかる時間

色々と脱線して他に調べものをしてしまったけれど作業自体は本来15分もあれば余裕で終わると思います。

AWSのS3で静的サイトとして公開するより楽なのではないでしょうか。 CloudFrontと接続とか考えなくて済みますし。

R2やS3で静的サイトとして公開することとの違い

やはり静的サイト公開専用機能として作られているので、細かい所を色々設定しなくて済むのは楽ですね。

プレビュー機能は自分は多分あまり使いませんが、履歴やロールバック機能もありますし。

CloudFrontもCloudflareの通常サービスも静的サイトに最適化されているわけではないので色々と取り消しとかが面倒ですが、 Pagesは機能を静的サイトに最適化していて考えることが少なくて良いですね。

それにうっかり有料版を使うことも無さそうですし。

wwwのリダイレクト

ncaq.netからwww.ncaq.netへのリダイレクトもCloudflareに任せたい所です。本来wwwは付けなければ良かったなと日々後悔しているのですが、今更URLを変えるわけにもいきませんね。

これは、メールサーバを自宅サーバから移行 · Issue #136 · ncaq/www.ncaq.net を終わらせてMXレコードを修正しないと出来ませんね。いや今のPostfix環境でも出来るんですが、どうせ弄るなら同時に移行したいです。なので後にします。

あと、 WKD - GnuPG wiki を設定するならこちらもPages作って、参照するもの以外をredirectするようにしますか? いや上から参照してマッチしないやつだけリダイレクトって出来るんだろうか? 出来ない気がしますね… 出来なかったらopenpgpkeyのサブドメインでもWKD出来るらしいから問題ないんですが。

他のホスティング

静的なものについてはどんどんPagesにして自宅サーバの負担を減らしたり、もうリダイレクトでGitHub見るようにしたいですね。 https://cdn.ncaq.net/とか。こういうのに関してはGitHub連携で自動デプロイさせた方が良さそう。

CI/CDについて

Cloudflare PagesのCDはHaskell未対応の都合上CDは厳しそうですが、逆にGitHub Actionsでwranglerを動かす形のCDは割とうまいこと動きそうです。

複数人で編集するサイトならばCloudflareのビルドに任せるよりそちらの方が良いかも?

wranglerについて

wranglerはあくまでCloudflare Workersなどを含めてサーバレスツール全般を管理するツールなので、 pagesを管理するには多機能過ぎて色々迷ってしまいますね。 Workersなどを使わない場合wrangler.tomlとか依存関係抱えて作るより、

yarn wrangler pages publish _site --project-name www-ncaq-net

ぐらいで済ました方が良さそうです。

将来性にも期待が持てる

CloudflareのWorkersやKVやR2やD1などが使えるため、将来的にサイトの一部をfetchしてSSRしたいなどの拡張要求が出てきても、無料枠の範囲が広いのでかなり融通が効きそうです。仮に有料枠に入っても格安ですし。