Programming

月88円でブログを運用する方法 ― Astro × AWS で個人サイトを建てた話

月88円でブログを運用する方法 ― Astro × AWS で個人サイトを建てた話

経緯:なんで自分でブログを建てようと思ったのか

YouTubeで活動しつつ、ガジェットのレビューやプログラミングの備忘録を書き残したいなと前から思っていた。 noteやはてなブログも考えたけど、エンジニアなら「自分のドメインで、自分が完全にコントロールできる場所」が欲しい。 あと正直なところ、インフラを自分で組むのが楽しそうだった。それが一番の動機。

技術スタック:何を使って作ったか

レイヤー技術選定理由
フレームワークAstroビルド時にHTMLを生成、JSほぼゼロで爆速
スタイリングTailwind CSSユーティリティファーストでサクサク書ける
記事管理MDXMarkdownの中にコンポーネントを埋め込める
ホスティングAWS S3静的ファイル置き場。安い
CDNAWS CloudFront世界中のエッジから配信
DNSAWS Route53独自ドメインの管理
CI/CDGitHub Actionsgit push で自動デプロイ

Astroを選んだ理由

WordPressは論外として(PHPもMySQLも要らないブログに大げさすぎる)、Next.jsやGatsbyも候補にあった。 でもAstroの「デフォルトでクライアントJSをゼロにする」という思想がドンピシャだった。

ブログ記事を読むのにReactのランタイムは要らない。 AstroならビルドしたらただのHTMLとCSSが出てくるので、S3に置くだけで完結する。

// Astroのコンポーネントはビルド時に実行されて、
// 出力されるのは純粋なHTMLだけ
const posts = await getCollection('blog');

インタラクティブな要素が必要になったら、そのコンポーネントだけ部分的にハイドレーションできる(Island Architecture)ので、将来の拡張性も問題ない。

AWSの構成と、なぜ月88円で運用できるのか

このサイトのインフラ構成はこんな感じ:

[GitHub] → push → [GitHub Actions] → build → [S3] → [CloudFront] → [ユーザー]

                                                   [Route53 DNS]

コストの内訳

普通のVPSやレンタルサーバーだと月1,000〜3,000円かかるところを、このサイトの月額コストはこうなっている:

サービス月額(アクセスほぼ無し時)
S3(ストレージ)約 1円
CloudFront(CDN)約 0円(無料枠内)
Route53(DNS)約 55円(ホストゾーン維持)
消費税等約 32円
合計約 88円

月88円。 缶ジュースすら買えない金額で、独自ドメインのブログが全世界に公開されている。

アクセスが増えても S3 と CloudFront は従量課金なので、月間数万PV程度なら数百円で済む。WordPressのレンタルサーバーに月額払い続けるのがバカらしくなるレベル。

セキュリティ面のメリット

静的サイトなのでサーバーサイドの処理が一切ない。つまり:

  • SQLインジェクション → DBがないので不可能
  • XSS → サーバーサイドでの動的生成がないので攻撃面が極小
  • WordPress系の脆弱性 → PHP動いてないし、プラグインもない
  • サーバーへの不正アクセス → SSHで入れるサーバーが存在しない

攻撃する場所がそもそも存在しないので、セキュリティ的にはかなり強固。

開発中にハマったこと

npm install が爆発する

Astroの公式テンプレートで始めたら、Tailwind CSS v4 と @astrojs/tailwind のバージョン不整合でインストールが通らなかった。

npm ERR! ERESOLVE could not resolve

結局 Tailwind CSS v3 に固定して --legacy-peer-deps で解決。このへんのnpmの依存関係地獄はNode.jsエコシステムの風物詩。

TypeScriptの型エラーと格闘

astro check を通そうとしたら型エラーが大量発生。tsconfig.jsonmoduleResolutionenv.d.ts の設定を何度もいじって、最終的に全部通した時の達成感はなかなかのものだった。

S3で「Access Denied」が出る

これが一番時間を溶かした。ブログ一覧やCIの記事リンクをクリックすると AccessDenied のXMLが返ってくる。

原因は、Astro のデフォルトビルド形式が directory/blog/post/index.html を生成)なのに対して、S3 の REST API エンドポイント経由でアクセスすると、サブディレクトリの index.html を自動的に返してくれないこと。

解決策は astro.config.mjsbuild.format: 'file' に変更して、各ページを /blog/post.html のような個別ファイルとして出力するようにしたこと。リンクにも .html 拡張子を明示的に付ける必要がある。

// astro.config.mjs
export default defineConfig({
  build: { format: 'file' },
  trailingSlash: 'never',
});

GitHub ActionsでAWS認証が通らない

GitHub Secrets の設定で、Repository secrets ではなく Variables タブに値を入れてしまっていた。さらに、3つのシークレット(AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEYAWS_S3_BUCKET)を1つの入力欄にまとめて入れようとしていた

それぞれ別のシークレットとして登録する必要がある。こういう初歩的なミスが一番時間を食う。

CI/CDパイプライン:push したら1分で公開

最終的に完成したワークフローはこれ:

  1. ローカルで記事を .mdx で書く
  2. git push する
  3. GitHub Actions が自動で npm run build を実行
  4. ビルド成果物を aws s3 sync で S3 にアップロード
  5. 約1分後に全世界で閲覧可能

記事を書く → push → 公開、という流れが完全に自動化されている。WordPressの管理画面にログインする必要すらない。

まとめ

  • Astro: 静的サイトに最適。JS ゼロで爆速
  • AWS (S3 + CloudFront): 月88円。セキュリティも強固
  • GitHub Actions: push するだけで自動デプロイ
  • ハマりポイント: npm依存関係、S3のAccess Denied、GitHub Secrets の設定ミス

個人ブログに月何千円も払う時代は終わった。 エンジニアなら自分で建てよう。月88円で済むんだから。