月88円でブログを運用する方法 ― Astro × AWS で個人サイトを建てた話
経緯:なんで自分でブログを建てようと思ったのか
YouTubeで活動しつつ、ガジェットのレビューやプログラミングの備忘録を書き残したいなと前から思っていた。 noteやはてなブログも考えたけど、エンジニアなら「自分のドメインで、自分が完全にコントロールできる場所」が欲しい。 あと正直なところ、インフラを自分で組むのが楽しそうだった。それが一番の動機。
技術スタック:何を使って作ったか
| レイヤー | 技術 | 選定理由 |
|---|---|---|
| フレームワーク | Astro | ビルド時にHTMLを生成、JSほぼゼロで爆速 |
| スタイリング | Tailwind CSS | ユーティリティファーストでサクサク書ける |
| 記事管理 | MDX | Markdownの中にコンポーネントを埋め込める |
| ホスティング | AWS S3 | 静的ファイル置き場。安い |
| CDN | AWS CloudFront | 世界中のエッジから配信 |
| DNS | AWS Route53 | 独自ドメインの管理 |
| CI/CD | GitHub Actions | git 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.json の moduleResolution や env.d.ts の設定を何度もいじって、最終的に全部通した時の達成感はなかなかのものだった。
S3で「Access Denied」が出る
これが一番時間を溶かした。ブログ一覧やCIの記事リンクをクリックすると AccessDenied のXMLが返ってくる。
原因は、Astro のデフォルトビルド形式が directory(/blog/post/index.html を生成)なのに対して、S3 の REST API エンドポイント経由でアクセスすると、サブディレクトリの index.html を自動的に返してくれないこと。
解決策は astro.config.mjs で build.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_ID、AWS_SECRET_ACCESS_KEY、AWS_S3_BUCKET)を1つの入力欄にまとめて入れようとしていた。
それぞれ別のシークレットとして登録する必要がある。こういう初歩的なミスが一番時間を食う。
CI/CDパイプライン:push したら1分で公開
最終的に完成したワークフローはこれ:
- ローカルで記事を
.mdxで書く git pushする- GitHub Actions が自動で
npm run buildを実行 - ビルド成果物を
aws s3 syncで S3 にアップロード - 約1分後に全世界で閲覧可能
記事を書く → push → 公開、という流れが完全に自動化されている。WordPressの管理画面にログインする必要すらない。
まとめ
- Astro: 静的サイトに最適。JS ゼロで爆速
- AWS (S3 + CloudFront): 月88円。セキュリティも強固
- GitHub Actions: push するだけで自動デプロイ
- ハマりポイント: npm依存関係、S3のAccess Denied、GitHub Secrets の設定ミス
個人ブログに月何千円も払う時代は終わった。 エンジニアなら自分で建てよう。月88円で済むんだから。