ブログを書いていると、「記事を書くだけでなく、もっと多くの人に届けたい」という気持ちが生まれてきます。
Google検索からの流入を増やすのは時間がかかります。そこで目を向けたのが、Xの自動投稿です。ブログ記事の要点をXで発信することで、検索以外の流入経路を作れるのではないかと考えました。
ただ、1日2回投稿を手動で続けるのは現実的ではありません。そこでXの自動投稿を仕組み化することにしました。
なお、実装の細かいコードはClaude Codeに相談しながら作りました。この記事では「何がどう繋がって動くか」の全体像に絞って解説します。
この記事の位置づけ:本記事はXの定時自動投稿の実装(Cloudflare Workers+GitHub Actions)に特化しています。投稿する記事の選定をAIに任せる話は「Xの自動投稿の"中身"をClaude Codeに任せる|毎朝ニュースから紹介記事を選ぶAI活用」で解説しています。
この記事でわかること
- X自動投稿の仕組みに登場するツール・サービスの役割を理解できる
- 最初にはまったGitHub Actionsの時刻ズレ問題とその解決策を把握できる
- 実装のキーワードを知ることで、Claude Codeなどを活用して自分でも仕組みを作れる
なぜX自動投稿をしようと思ったのか
ブログの流入経路は、現状ほぼGoogle検索一本です。SEOで順位が上がれば読者は増えますが、それには時間がかかります。
検索以外のルートとして考えたのがXです。投資・資産形成の話題は、Xでもアクティブに情報交換されています。ブログ記事の要点を短くまとめてXで発信することで、
- ブログを知らない人にリーチできる
- 記事へのリンクをXから踏んでもらえる
という効果が期待できます。
「1日2回投稿する」という目標を設定しましたが、毎日手動でやるのは続きません。であれば自動化してしまおう、というのが出発点です。
登場人物:どのツールが何をしているか
自動投稿の仕組みには、次の5つの登場人物(ツール・サービス)が出てきます。先に役割を押さえておくと、このあとの話が追いやすくなります。下の図の①〜⑤と対応しています。
自動投稿の起点はCloudflare Workers(タイマー)。GitHub Actionsが投稿処理を実行し、X API経由でXに届く。ブログ記事の公開・キュー登録は事前準備にあたる
① ブログ(Hugo)— 投稿のネタを用意(準備役)
このブログは Hugo で作っています。記事を書いて公開すると、それが「Xでお知らせするネタ」になります。自動投稿は、ここで用意された記事を後から取り出して発信する仕組みです。
② Cloudflare Workers — タイマー=自動投稿の起点
このブログは Cloudflare Pages で公開しています。同じ Cloudflare の中にCloudflare Workersという「自分でサーバーを用意しなくてもクラウド上でプログラムを動かせる仕組み」があります。自動投稿では、この Workers が毎日の決まった時刻に号令をかけるタイマーとして働きます。自動投稿の起点はここで、Workers 自身が投稿するわけではありません。なぜ Workers がタイマー役なのかは、次章の試行錯誤で説明します。
③ GitHub Actions — 投稿処理を実行する実務役
ブログのソースコードは GitHub で管理しています。GitHub にはGitHub Actionsという自動化機能があり、②の Workers から呼び出されると実際の投稿処理を行います。投稿待ちリスト(キュー)を読み取り、X API へ投稿します。APIキーなどの機密情報も GitHub 側で安全に保管します。
④ X API — 投稿リクエストの窓口
Xに自動投稿するには、X APIという「外部からXを操作するための窓口」を使います。③の GitHub Actions からのリクエストを受けて、X に投稿します。利用にはXの開発者プログラムへの登録と認証キーの取得が必要です。料金は投稿数に応じた従量課金制で、実際にこのブログのXアカウントで運用した実績では、直近30日間の請求額は約12ドル(総リクエスト数63件)でした。1日2回・月60件程度の自動投稿であれば、月あたり2,000円弱というのが実測ベースの目安です(料金体系は変わりやすいため、最新情報はX Developer Platformで確認してください)。
⑤ X(旧Twitter)— 投稿の公開・拡散
最終的に、投稿が X 上で公開され、フォロワーのタイムラインに届いて拡散されます。
最初の設定:GitHub Actionsで試みた
まず試みたのは、GitHubのCron(定時実行)機能を使ったGitHub Actionsによる自動投稿です。
GitHub Actionsには「毎日8時と20時に実行する」といった設定を書くだけで定時実行ができる機能があります。設定ファイル(YAML形式)を書いてGitHubに置くだけなので、仕組みとしては比較的シンプルです。
しかし、実際に動かしてみると大きな問題が発覚しました。
問題:時刻のズレが大きすぎる
設定では「朝8時に投稿する」としていたにもかかわらず、実際に投稿される時刻が読めません。早ければ8時数分後、遅ければ11時になることもありました。
左:GitHub Actionsは混雑時に大幅な遅延が発生。右:Cloudflare Workersはほぼ予定時刻通りに安定して実行
なぜこうなるか? GitHub Actionsのcronは、GitHubのサーバーが混雑しているときにキュー(順番待ち)に入ります。無料枠・共有サーバーの仕様上、ズレは「仕様の範囲内」とされており、保証はありません。多くのユーザーが使う時間帯(UTCで整時など)は特に遅延しやすいことが知られています。
「投稿時刻を気にしなければいい」と思えればそれまでですが、SNS投稿のエンゲージメントはタイミングに左右される面があります。朝の通勤時間帯に届けたいのに昼以降に投稿されては意図通りになりません。
改善策:Cloudflare Workersに移行する
解決策として選んだのが、GitHub ActionsからCloudflare Workersへのタイマー処理の移管です。
投稿スクリプト自体(どんな文章をXに送るか)はGitHubで管理しつつ、定時実行するタイマーはCloudflare Workersに任せるという構成に変えました。
Cloudflare WorkersにはCron Triggersという機能があり、ほぼ予定時刻通りに安定して実行されます。GitHub Actionsのような数十分〜数時間レベルの遅延は発生しません。また、このブログのサーバーと同じCloudflareのインフラ上で動くため、設定の一元管理という副次的なメリットもあります。
構成の全体像(改善後)
ここで正確に説明すると、Cloudflare Workersは「投稿コードをそのまま実行する」わけではありません。定刻になったらGitHub ActionsをAPI経由で起動するタイマーとして機能しています。実際の投稿処理はGitHub Actions上のプログラムが担当し、APIキーもGitHubの暗号化された変数管理機能(GitHub Secrets)で安全に保管されています。
- Cloudflare Workers(Cron Triggers):朝・夜の指定時刻に起動し、GitHubのAPIを叩いてGitHub Actionsを起動する
- GitHub Actions:起動されたら投稿キューの先頭を読み取り、X APIで投稿する
- X API:リクエストを受け取りXに投稿する
- X:フォロワーのタイムラインに届く
「タイマーの信頼性はCloudflare Workersに任せ、実処理はGitHub Actionsが行う」という役割分担です。
GitHub ActionsとCloudflare WorkersでCronの「確かさ」がこれほど違うとは、やってみるまで気づきませんでした。
セキュリティ面で見た「GitHubに寄せる」設計
「タイマー(Cloudflare Workers)→実処理(GitHub Actions)→投稿(X API)」という役割分担になっています。ここで「なぜCloudflare WorkersからX APIを直接叩かないのか」と疑問に思う方もいるかもしれません。
理由の一つはセキュリティです。
X APIで投稿するには4種類の認証情報(APIキー・シークレット・アクセストークン・アクセストークンシークレット)が必要です。この情報が漏洩すると、第三者があなたのXアカウントに自由に投稿できてしまいます。
GitHubにはGitHub Secretsという機能があり、APIキーなどの機密情報を暗号化して保管できます。保管した値はGitHub Actionsの実行時にのみ取り出せる環境変数として使われ、コードには一切出てきません。リポジトリを見ても、キーの中身は確認できません。
さらに、実行ログ・変更履歴・APIキーの管理をGitHub一か所に集約できるというメリットがあります。
| 確認したいこと | 確認できる場所 |
|---|---|
| 過去の実行が成功したか | GitHub Actions の実行履歴 |
| 投稿内容・投稿日時 | data/x-post-history.yaml(コミット履歴) |
| コードの変更履歴 | git log |
Cloudflare WorkersにはGitHub起動キーのみ、X APIの鍵はGitHub側だけで管理する
Cloudflare Workersを「タイマー専用」に限定することで、Workers側にはGitHubへのアクセスキー(PAT)だけを持たせれば済みます。X APIの認証情報はGitHub側のみで管理できるため、「何がどこにあるか」が整理され、万が一のときの影響範囲を絞り込みやすくなります。
補足:記事が実際に公開されたタイミングでキュー登録する
投稿タイミングの問題が解決したところで、次に考えたのが「何を投稿するか」の管理です。
なぜ「キュー(投稿待ちリスト)」という仕組みにしたのか
Xへの投稿目的はブログの流入経路を増やすこと。具体的には、新着記事が公開されたらXでお知らせするという使い方を想定していました。
最初に思い浮かぶ実装は「記事が公開されたら即座にXへ投稿する」です。しかしこれだと、記事を連続して公開したときに立て続けにXへ投稿されてしまい、フォロワーのタイムラインに一気に流れ込む形になります。
そこで採用したのが、投稿候補を一度リストに溜めておき、決まった投稿スロットで1件ずつ取り出して投稿するという仕組みです。記事の公開ペースに関係なく、新着記事の告知が一定のペースで届けられるため、フォロワーへの露出がバラけて継続的にリーチできます。
この「投稿候補を溜めておくリスト」が投稿キュー(投稿待ちリスト)です。投稿する記事の場所を列挙した管理ファイルを用意し、夜の投稿スロットでCloudflare Workersに呼び出されたGitHub Actionsが、その先頭から1件ずつ取り出して投稿する、という先入れ先出し方式です。なお、朝の投稿スロットはこのキューを使わず、既存記事からのおすすめ紹介を担当しています(選び方は姉妹記事で解説しています)。
# 投稿待ちリストのイメージ
queue:
- post_path: content/posts/記事A.md
added_at: '2026-04-29T11:00:00+09:00'
- post_path: content/posts/記事B.md
added_at: '2026-04-30T09:00:00+09:00'
ブログ公開と同時にキュー登録する仕組み
「記事が公開されたら投稿キューに追加する」という仕組みを作ろうとしたとき、ブログの予約投稿機能との不整合が問題になりました。
このブログには「予約投稿」の機能があります。記事ファイルの冒頭に公開日時を書いておくと、その日時まで本番サイトに表示されない仕組みです(毎朝の自動ビルドで公開)。このブログの公開フローについては別記事で詳しく解説しています。
ここで不整合が生まれます。ブログを本番公開フォルダ(GitHubのpostsフォルダ)にアップロードした瞬間に投稿キューへ登録してしまうと、記事がまだ公開前なのにXへの告知が先に出てしまうリスクがあります。たとえば翌朝公開予定の記事を夜にアップロードしたとき、その夜のうちにXへお知らせが届いてしまう、という状況です。
この問題への対処として、キュー登録のタイミングを「アップロードした瞬間」ではなく「記事が実際に公開された瞬間」に変えました。具体的には以下の2段階で制御しています。
① アップロード時:記事の公開日時をチェックしてスキップ
本番公開フォルダにブログ記事が追加されたとき、GitHub Actionsのワークフローが記事ファイルの冒頭に書かれた公開日時を確認します。公開日時が未来の場合はキュー登録をスキップします。実際の登録は②の自動サイト更新が行います。
② 朝の自動ビルド(Cloudflare Workerが起動):公開になった記事を検知して投稿リストに登録
朝の自動ビルド(Cloudflare WorkersのCron Triggersが起動)のタイミングで、公開フォルダにある記事を確認します。公開日時が現在時刻以前の記事を見つけたら投稿待ちリストへ追記します。なお、このCloudflare Workersは②で紹介したX投稿用のタイマーとは別に、ブログ公開のために毎朝走っているタイマーです(詳しくはCloudflare Pagesのブログ公開フローで解説しています)。すでにXに投稿した記事が再び登録されることはありません。
pushした時点ではスキップし、記事が実際に公開されたタイミングでキューに登録される
この仕組みにより、「記事が公開される前にXへの告知が出てしまう」という事態を防ぎつつ、公開後は自動でXへの投稿が予約されます。
実際に起きた失敗:過去記事が一括で登録されてしまった
cronによるキュー登録の仕組みを導入した直後、ダッシュボードを見たら過去記事が数十件、一気にXの告知キューに入っていたという事態が発生しました。
原因はcronのスキャン範囲にありました。
投稿済み履歴ファイルに記録がなかった過去記事がすべて「未投稿」と判断された
投稿スクリプトには「投稿待ちリストにある」「投稿済み履歴ファイルにある」記事を重複して登録しない仕組みがあります。ただし、この仕組みを導入する以前の記事は投稿済み履歴ファイルに記録がなく、すべて「未投稿」と扱われてしまいました。
対処として2点修正しました。
まず、投稿待ちリストに登録されていた記事を手動でクリアしました。次に、cronのキュー登録条件を変更しました。
時間窓を設けることで、過去記事の一括登録を防ぐ
自動ビルドは毎朝の決まった時刻に走るため、公開予約の記事を取りこぼさないよう、起動の遅延も見込んで検知の時間窓を2時間に設定しています。重複登録は投稿済み履歴ファイルの確認で防がれるため、2時間の幅があっても問題ありません。
この修正により、「cronが初めて走ったときに全過去記事がキューに入る」という事態は起きなくなりました。仕組みを導入するタイミングによっては、投稿済み履歴ファイルに記録が残っていない記事が存在することがあります。時間窓フィルタはそのような場合の保険としても機能します。
Xの自動投稿文はブログ記事の設定に書いておくだけ
「X投稿の文章はどこで管理するのか」というと、実はブログ記事ファイルの冒頭に書いている設定がそのまま使われます。
記事ファイルの冒頭(frontmatter)に書いたdescriptionとtagsが、X投稿文の材料になります。
---
title: "記事のタイトル"
date: 2026-05-01T08:00:00+09:00
description: "ここに書いたテキストがX投稿の本文になります。"
tags: ["ハッシュタグ1", "ハッシュタグ2", "ハッシュタグ3"]
---
生成される投稿文の構成はおおむね以下の通りです。
descriptionが本文に、tagsがハッシュタグに自動変換される
記事を書く段階でX投稿を意識し、descriptionを140文字以内に収まるよう書いておくことで、そのまま投稿文として使えます。ブログ記事に丁寧なdescriptionを書く習慣が、そのままXの投稿品質に直結するという仕組みです。
実装のキーワード(Claude Codeを使えばできる)
細かい実装はClaude Codeに相談しながら進めました。「何を作ればいいか」を理解した上でClaude Codeに依頼すると、コードを書いてもらいながら仕組みを動かすことができます。
自分でも手を動かして再現したい方向けに、実装キーワードの一覧・始める前に必要なもの・Claude Codeへの依頼例をまとめました。読み物として読みたいだけの方は、次の「仕組みは動かしながら育てるもの」まで読み飛ばしても問題ありません。
実装キーワード集・必要なもの・Claude Codeへの依頼例を見る(クリックで展開)
実装を進める際に登場するキーワードをまとめておきます。これらを知っておくと、Claude Codeに相談するときの言語化がしやすくなります。
| キーワード | 意味 |
|---|---|
| X API v2 | XをプログラムからWrite操作するためのAPI(第2世代) |
| OAuth 1.0a | X APIの認証方式。APIキー・シークレット・アクセストークンの4値で認証する |
| GitHub Actions | GitHubの自動化機能。YAMLで条件・処理を定義する |
| workflow_dispatch | GitHub Actionsを外部APIから手動/プログラムで起動するトリガー |
| Cloudflare Workers | Cloudflareのサーバーレス実行環境。JavaScriptで動く |
| Cron Triggers(Cloudflare) | Cloudflare Workersの定時実行機能。UTCで設定。GitHub Actionsのような大幅な時刻ズレは発生しない |
| Wrangler | Cloudflare Workersをパソコンのコマンド操作で管理するツール。作ったプログラムをCloudflareに送り込むときに使う |
| FIFO キュー(YAML) | 投稿待ちリスト。先に追加した記事から順番に投稿される |
| paths フィルター | GitHub Actionsの起動条件の絞り込み。特定のフォルダ内のファイルが変更されたときだけ動くよう設定できる |
| GitHub Secrets | APIキーなどの機密情報を暗号化して管理する仕組み。Actions内で環境変数として使える |
実際に手を動かす前に、揃えておきたいものを整理しておきます。
始める前に必要なもの
- Xアカウント:開発者プログラムへの登録と認証キーの取得が必要
- GitHubアカウント:無料
- Cloudflareアカウント:無料枠の範囲で運用可能
- Claude Code:契約プランが必要
想定コスト目安:X APIは実測で月あたり2,000円弱(前述)。GitHub・Cloudflareは今回のような使い方なら無料枠に収まります。
Claude Codeへの依頼例としては、次のような形が有効です。
「Cloudflare WorkersのCron Triggersを使って、毎日7時30分(JST)にGitHub Actionsをworkflow_dispatchで起動するスクリプトを作ってください。Cron TriggersはUTCで設定するため、7:30 JSTは22:30 UTCに変換してください。GitHub APIのトークンはCloudflareの環境変数から読み込む形にしてください。起動されたGitHub Actionsは、data/x-queue.yamlの先頭エントリを読み取ってX APIで投稿し、投稿後はキューから削除してコミットする処理を行います。」
このような依頼文を出せれば、実装の骨格はClaude Codeが書いてくれます。
仕組みは動かしながら育てるもの:監視を続けることが大切
「設定すれば後は自動で動く」と思えますが、実際には一度作って完成にはなりません。
この記事自体がその証拠です。仕組みを実際に動かしていく中で、次々と問題が出てきました。
- GitHub ActionsのCronで時刻がズレる → Cloudflare Workersへ移行
- 過去記事が数十件、一括で投稿待ちリストに登録される → 時間窓フィルタの追加
- 朝の本投稿とバックアップ枠で二重投稿が発生する → 重複チェックロジックの修正
特に二重投稿の問題は「特定の条件が重なったときだけ起きる」という性質で、最初は原因の特定にも時間がかかりました。こうした問題は、動かしてみて初めて気づくものがほとんどです。
Xの投稿結果を確認する習慣が大切
実際のところ、確認として一番手軽で確実なのはXのタイムラインを定期的に目で見ることです。
時刻が意図通りか・投稿文に問題がないか・同じ記事が重複していないか——これらはXを見るだけで気づけます。「自動投稿が止まっていた」「同じ記事が2回投稿されていた」といった問題も、Xを普段から確認していれば早期に発見できます。
「自動化 = 放置できる」ではなく、「自動化 = 作業は減るが、結果の確認は必要」というのが率直な実感です。
まとめ
- 目的:ブログ記事をXで定期発信するため、1日2回の自動投稿を仕組み化した
- タイマーの問題:最初に使ったGitHub Actionsは混雑時に投稿時刻が数時間ズレることがあり、Cloudflare Workersに乗り換えて解決した
- 役割分担:定時に動かすタイマーはCloudflare Workers、実際の投稿処理とAPIキーの管理はGitHubにまとめている
- 投稿文の準備:記事を書くときにdescriptionを140文字以内で書いておくだけで、そのままX投稿文になる
- 実際に起きた失敗:仕組みを入れた直後に過去記事が数十件まとめて投稿キューに入ってしまい、対象を直近2時間以内の記事に絞ることで修正した
- 確認の大切さ:自動化しても定期的にXのタイムラインを目で確認することで、問題を早く見つけられる
「自動化したい」という気持ちはあっても、どのツールが何をするのかが見えていないと最初の一歩が踏み出しにくいものです。この記事の図解で全体像が掴めた方は、ぜひClaude Codeを使った実装も試してみてください。
この「定時に投稿する土台」ができた後の話として、「何を投稿するか」という次の課題があります。記事の順番を決め打ちにすると今の話題から取り残されますが、毎日手で選ぶのも続きません。その課題を解決するため、Claude Codeにニュースを読ませて話題に合った記事を自動選定させる仕組みを作りました。詳しくは続きの記事で紹介しています。
何かの参考になれば幸いです。
関連記事
- Xの自動投稿の"中身"をClaude Codeに任せる|毎朝ニュースから紹介記事を選ぶAI活用 — この記事の次のステップ。投稿する記事の選定をClaude Codeがニュース連動で自動化する仕組み
- Cloudflare Pagesのブログ公開フロー|Draft確認と予約投稿の仕組み — 本記事で触れた「朝の自動ビルドで記事が公開される」仕組みの解説