Becalm Tech BlogTECH BLOG
インフラ

サーバーを1台も持たないテックブログのインフラ構成

石塚 凪(代表取締役社長 兼 CEO)公開日: 最終更新:

コメント欄・会員機能・サイト内検索を持たないテックブログなら、必要なものは静的ファイルの配信だけです。データベースもアプリケーションサーバーも要りません。このサイトは S3 + CloudFront だけで動いています。

逆に、これらの機能が要件に入るなら話は変わります。本記事は上記を持たない前提での構成です。問い合わせフォームのように単発で必要になるものは、外部SaaSに寄せて静的のまま維持しています。

構成

  • S3: 非公開バケット。OAC(Origin Access Control)でCloudFrontからのみ読める
  • CloudFront: 配信とキャッシュ。CloudFront Functions で index 補完とアクセス制限
  • Route 53: 既存ホストゾーンにレコードを追加するだけ
  • GitHub Actions: ビルドとデプロイ。OIDCでキーレス認証
  • Terraform: 上記すべてを管理(手動作成は禁止)

常時起動するリソースが無いため、費用はストレージ量とリクエスト数にほぼ比例します。数十MBの静的アセットと月数千リクエスト程度を想定した設計目標は月あたり十数円の水準で、CI/CDもGitHub Actionsの無料枠に収めています。

運用実績が積み上がる前の見積もりなので、実測値ではありません。トラフィックや画像の量が増えれば転送量とキャッシュ無効化の分だけ上がります。

キャッシュ制御を3段階に分ける

デプロイで一番事故が起きやすいのが、HTMLは新しいのに参照先のアセットがまだ無いという中間状態です。同期の順番とキャッシュ設定を分けて防ぎます。

.github/workflows/deploy.yml

# 1) ハッシュ付きアセット: 永久キャッシュ
aws s3 sync out/ "s3://$BUCKET" --exclude "*" --include "_next/static/*" \
  --cache-control "public, max-age=31536000, immutable"

# 2) 画像・フォント等: 1日キャッシュ
aws s3 sync out/ "s3://$BUCKET" --exclude "_next/static/*" --exclude "*.html" \
  --cache-control "public, max-age=86400"

# 3) HTMLは最後 + 残骸を整理
aws s3 sync out/ "s3://$BUCKET" --exclude "*" --include "*.html" \
  --cache-control "public, max-age=0, s-maxage=300" --delete

デプロイ用の認証情報を持たない

GitHub Actions から AWS を操作するのに、アクセスキーを Secrets に置く必要はありません。OIDCで一時credentialを受け取る方式にすれば、期限の無い認証情報を保管しなくて済みます。

ただし安全性はIAMロール側の設定次第です。信頼ポリシーでリポジトリとブランチを条件に絞ること、付与する権限をデプロイに必要なS3とCloudFrontの操作だけにすることが前提になります。ここが緩いと、キーを置かないことの意味が薄れます。

.github/workflows/deploy.yml

permissions:
  id-token: write # OIDCによるキーレスAWS認証
  contents: read

公開前のアクセス制限

制作中のサイトを検索エンジンや第三者に見せないため、CloudFront Functions でBasic認証をかけています。noindex に頼るより確実で、追加のリソースも要りません。公開時に変数を切り替えて外します。

成果物に秘密情報が混ざっていないか検査する

静的サイトの怖いところは、ビルド成果物に入ったものはすべて公開されることです。ビルド用のAPIキーをうっかりクライアント側の環境変数に入れると、そのままJSに埋まって配信されます。

.github/workflows/deploy.yml

if grep -rlF "$BUILD_API_KEY" out/ 2>/dev/null; then
  echo "::error::ビルド用APIキーが成果物に混入しています"
  exit 1
fi

よくある質問

Q. 静的サイトでも問い合わせフォームは作れますか?

作れます。サーバーを持たないので、フォームの受け口は外部SaaSに任せる形になります。送信先を外部にするだけで、ページ自体は静的なまま維持できます。

Q. 記事が増えてもビルドは遅くなりませんか?

ページ数に比例してプリレンダリングの時間は伸びますが、CMSからの取得をビルド前の1回にまとめておけば、ボトルネックになるのはHTML生成だけです。Next.jsはこれを複数プロセスで並列に行うため、数百ページ規模までは実用的な時間に収まります。

まとめ

「サーバーを持たない」は制約ではなく、運用対象を減らす設計判断です。実行中のアプリケーションが無くなるぶん、監視と障害対応の対象は確実に小さくなります。

とはいえ運用がゼロになるわけではありません。証明書の更新、ビルド環境の依存パッケージの脆弱性対応、アクセス制御とキャッシュ設定の点検、配信のログ監視は残ります。減るのは「常時動いているものの面倒を見る」手間であって、定期点検そのものではありません。

それでも、コンテンツを書くことに時間を使えるようになるのがこの構成のいちばんの利点です。

Becalmで、一緒につくりませんか。

エンジニア・デザイナーを募集しています。カジュアル面談も歓迎です。