Astro + Cloudflare Pageでブログを作成した

cover

Astro + Cloudflare Pagesを利用して技術ブログをつくりました。その備忘録です。

Astro

2024年の技術ブログといえば、無難にAstroだろうという安易な理由で選択しました。もはや個人ブログレベルで、SSGにすることは当たり前ですし、今どきのフロントエンドの当たり前を、当たり前のように実行してくれる堅実なフレームワークだと思いました。特に選んで後悔はしていません。

で、やはりサイトを作成するからには、SEO対策もしっかりせねばならないと思っています。なかでもパフォーマンスは静的なサイトなのであまり問題にはならないですが、そのほかの諸々細かい設定は、やはりエコシステムが整っていないと使い続けるのも難しいです。

例えば、WordPressであれば連携なりプラグインなりが大量にあるので困ることはないのですが、それに似た仕組みが導入する技術にはあってほしいです。AstroではIntegrationという仕組みで最低限揃っているので非常に助かりました。

良かったこと

Integrationが便利

前述した通り、サイトマップやRSS対応など地味に面倒な処理を基本的には公式が用意されているためそれを利用すればよいです。逆にいえば用意されていなければ使えないわけで、WordPressほど充実しているわけでもないので注意。困ったことは後述しています。

astro.config.mjs というファイルに以下のように設定するだけです。自分の場合は mdx拡張(コードを shiki のルールでハイライトしてくれる)、通信時の圧縮、サイトマップ、CSS用のTailwind拡張、クローラ制御のための robots.txt 拡張を導入しています。増やした方がよいものなどあればぜひぜひ教えてください🙆

integrations: [
    mdx({
      syntaxHighlight: "shiki",
      shikiConfig: {
        theme: "dracula-soft",
        wrap: false,
      },
      drafts: true,
    }),
    compressor({
      gzip: true,
      brotli: true,
    }),
    sitemap(),
    tailwind(),
    robotsTxt(),
  ],

独自コンポーネントをマークダウンに埋め込める点

大前提としてブログはマークダウンで書きたいです。いまはNotionで書いた成果物をマークダウンとして出力し、それを下敷きにマークダウンの整理整頓をVS Codeで修正する、というプロセスをとっています。

最初からマークダウンでもいいのですが、やはり書く作業自体は、リッチエディタの方がテンション上がりますし、ポータビリティ的な観点で最終的にはマークダウンにしたい、というのが個人的なFAになっています。あとNotion AIに校正もしてほしかったり。

で、本題ですが、Astroでは記事を書く際にマークダウンに mdxというファイルを選ぶことができます。もちろん通常のマークダウンとしても書くこともできますが、独自の記法を用いることで、作成したコンポーネントを直接利用することが可能です。

例えば、zennであれば、 :::message という独自記法で警告のコンポーネントを描画できますが、似たようなコンポーネントを自前で作成し、 <Warning> というコンポーネントとして利用しています。

<Warning>こんな感じに書くと...</Warning>

またこのように mdx にコンポーネントを設定しなくとも、特定のマークダウン形式を書けばコンポーネントに変換してくれる文法拡張もできるようですが、ちょっとだけ調べて面倒だったのでやっていません。現時点ではやめただけなので、必要ならやるかも。まあ、独自コンポーネントはポータビリティを損なうという意見も(以下略)

残念だったこと

細かいところに手が届かない

仕方ないといえば仕方ないのですが、細かいところに手が届かない点があります。例えばリンクカードです。

見栄えのためにURLを直で貼るのではなく、リンクカードにしたいケースがしょっちゅうあります。はてなブログでも埋め込み形式を選べましたし、zennならURLを貼るだけでよく非常に楽でした。

自前でリンクカードは実装するのがやや面倒です。ビルド時に指定したサイトのmeta情報を取得して、こだわろうとすればOGPを取得する必要があったりもします。さらに一度取得したキャッシュして…ということまで考えだすとなかなかしんどい。が、よき実装が見つからず、ひとまず自分は以下のようなリンクカードのコンポーネントを自作しました。faviconとタイトルと概要を取得します。本当はOGP画像も取りたいのですが…自前だとやっぱりこのあたりが面倒なんですよね。

他にも、目次なんかも同様です。結局デザインまでお気に入りのものは、最終的には自作するしかないんですけどね…!

Safari(WebKit)では、 ViewTransition が機能しない

ViewTransition とはページの遷移時にアニメーションを自動でつけてくれる仕組みです。headタグに埋め込むだけでそれっぽい挙動をしてくれるため、フェードをかけてくれるため、ページが滑らかに遷移します。しかし Safari (WebKit)では対応していないようで、普段使いするiPhoneではその恩恵を得ることができませんでした。残念。Issueは挙げられていますが、現状では自動でフォールバックされるだけのようです。

Cloudflare Page

NetlifyやFirebase HostingによるWebページのホスティングもしたことがありますが、たかが個人ブログですし、いざとなったら変えられるので、興味本位でCloudflareに入門してみることにしました。

良かったこと

超簡単にデプロイできた

全く難しい設定がなく、デプロイまでは一瞬でした。GitHubからの連携を設定し、 main ブランチにプッシュ時にnpm run build して、生成物を /dist と設定するだけ。するとCloudflare側でデプロイしてくれる超簡単な仕様です。

Build Setting

http → https のリダイレクトの設定も初期設定で可能です。またカスタムドメインについても購入したドメインを設定するだけです。自分の場合は、お名前ドットコムの「他社のネームサーバを利用」という機能を利用して、Cloudflareのネームサーバを登録するのみです。ちなみにwww ドメインを non-www にリダイレクトしたいのですが、解決策に至っておりません。www のホストがAレコードでお名前ドットコム側に登録されており、この設定を変更する方法がよくわかっていません。これをCloudflareにリダイレクトする方法がよくわからず…誰か教えてください…😢

ややトリッキーな点としては、ビルド環境のタイムゾーンがGMT基準となっているため、生成物の時刻が9時間分遅れます。そこで環境変数に TZAsia/Tokyo に設定する必要があります。言語設定を変えるだけではダメなので注意。

Time zone

Cacheの設定もイージー

Cloudflare Pagesでは拡張子に応じて、自動でCDNでキャッシュをしてくれます。以下の拡張子のファイルは自動でよしなにキャッシュされます。

Cache Default ExtensionCloudflareドキュメント

現時点では「いいね」や「コメント」などの動的な機能もないため、ブログの更新やコンポーネントの更新といったタイミング、つまりデプロイ時に完全なHTMLを生成できるため、基本的にはすべてコンテンツのすべてをキャッシュ可能です。ただしHTMLはデフォルトではキャッシュされません。というわけで設定を追加する必要があります。Cloudflare の 「Cache Rules」という設定項目を変更しなければなりません。

Cache Setting for edge serverエッジキャッシュの設定

エッジTTLと呼んでいるものがいわゆるエッジサーバ(CDN)のキャッシュです。ページごとに応じたキャッシュを設定することも当然できますが、別に一括にキャッシュさせてしまってよいと思ったので、「キャッシュ制御ヘッダーを無視して、このTTLを使用します」を選択しています。

ただクライアントキャッシュ(ブラウザキャッシュ)のみは注意が必要なので、Astro側でCache-Control: public, max-age=0, must-revalidate になるように確認済み。

Cache Setting for clientクライアントキャッシュの設定

で、エッジサーバで一定期間キャッシュされますが、デプロイ時にはコンテンツが更新されている可能性もあるため、明示的にサーバサイドのキャッシュをパージしなければなりません。Cloudflare側に設定で出来ないものか、悪戦苦闘していたのですが、現時点では見つけることができませんでした。

あまり他のサービスとの連携は避けたかったのですが、仕方なしにGitHub Actionを用いて main ブランチのデプロイ時に限って、キャッシュをパージするようにしています。すでに作成されている方がいたため、以下のように workflow のファイルを作成し、シークレットをリポジトリに設定するだけです。とっても簡単で最高👏

name: Cloudflare Purge Cache
"on":
  push:
    branches:
      - main

jobs:
  purge_cache:
    runs-on: ubuntu-latest
    steps:
      - name: Cloudflare Purge Cache
        uses: jakejarvis/cloudflare-purge-action@master
        env:
          CLOUDFLARE_ZONE: ${{ secrets.CLOUDFLARE_ZONE }}
          CLOUDFLARE_TOKEN: ${{ secrets.CLOUDFLARE_TOKEN }}

キャッシュについては、CF-Cache-Status というレスポンスヘッダ―が HIT となっていれば成功です✌ちなみにキャッシュが設定されていない場合は DYNAMIC と表示になります。

Cache Setting for edge serverHTTPレスポンスヘッダを確認すればOK

残念なところ

実はいまのところあまりないです。

強いていうなれば、初期設定からのデプロイ体験までは非常にスムーズでしたが、あれこれの設定はどこにあるのだろう、というほど項目が多いです。例えば、前述したようなタイムゾーンやキャッシュの設定を探すのには一苦労でした。似たような設定項目がいくつかあるため、迷うこともありました。どれで設定すればいいのかという感じです。それほど多機能であることは間違い無いのですが、Netlify や Firebase Hosting のほうがインターフェースはシンプルでわかりやすいとは思いました。

使っていくとまた残念なところも見えてくるかもしれません。

さいごに

画像の多いブログ記事のページでもPCだと、パフォーマンス観点で100点を取れます。下記の計測はGitを高速化する技術のページです。

Lighthouse for desktopDesktop Lighthouse

モバイルで計測しても94点と高スコアなので十分でしょう。

Lighthouse for mobileMobile Lighthouse

パフォーマンスとしても文句なく、何より楽しく開発できましたし、これからも機能を追加していこうと思います。みなさまの参考になれば幸いです👌