WordPressの記事が公開された瞬間、Xにも自動投稿できないかn8nをつかって考えてみた話

ブログを投稿した後、X用にまた文章を書き直してハッシュタグ入れて投稿するのは大変マメな作業でありまして、極力自動化をしたいとおもうスーツマン。

「自動でAIが投稿してくれないかなぁ」

と思って、まず既存の有料サービスやプラグインを片っ端から調べてみました。

でもなんかどれもしっくりこなかったり設定がめんどくさかったりで。また、なんか自動投稿でボットとみなされるとバンされる恐れもあるみたいな情報も。

そんな時、CLAUDE君に相談です。

結論、n8nを使えばXの有料会員でAPI自動投稿できるっぽいですが無料アカウントの場合は私がやりたかった完全自動投稿はできませんでした。

なので代替え策としてGmail で投稿文章とハッシュタグをn8nに自動作成させて自分で投稿文章確認してから、ワンボタンで投稿画面を開いてポストするだけにしてみましたが、まぁこれでもほぼ何もせず2回ボタン押すだけで自動投稿できるようになりました。

わはは♪( ´θ`)ノ

スポンサーリンク

有料サービスの壁に全部ぶつかった話

X API直接連携:月15,000円は無理

X(旧Twitter)のAPIを直接使えば一番シンプルだろうと思って料金を調べたら、無料プランにAPIが使える感じの記載がなくなってました。Basicプランで月100ドル、日本円で約15,000円。(2026年8月調査時点)

そこまでたくさん投稿するわけでもなく、ブログもいまは月に多くて10−20投稿なのでさすがに厳しい金額だなと思いつつも、投稿の部分だけ何とかならないか一縷の望みをかけて、ひとまずn8nで直接繋いでみることにしました。

IFTTT:無料ではもう動かせない

昔はよく無料の自動化サービスといえばIFTTTをよく使ってました。これだよなと思って開いたら、WordPressトリガーもRSSトリガーもやりたいことをやろうとすると、Proが結局一番使いやすくなってる・・・まぁそれはそうですよね。

自動投稿できることはできるけど、管理が必要そうで、またRSSで判断させて瞬時に投稿してくので、編集とか何か変えたら再投稿されたりするとXのスパム認定もうけそう。 ということでこちらもやめました。

Buffer:自動化だけ有料プラン

Bufferも調べました、自動化(RSS→X投稿)は有料プラン(1チャンネルあたり)が制限が少なくなり使っていくには余分に費用が必要でした。

使えると思ったら使えばいいと思うんですが結局管理画面やマイページで予約投稿とか、設定とか開いて手動でキューに追加する必要あります。まぁそれだけなら無料で1日投稿数の制限はありますが、使えるそうです。

でも理想はなーんにもしてなくても、予約公開された瞬間にパッと

できることなんですよ(*´ー`*)

一時しのぎ:CLAUDE君にコピペしてもらう

というわけで、一旦CLAUDE君(Claude Code)にX投稿文を生成してもらって、それを手でコピペして投稿する運用にしました。

流れは簡単で、この記事のX無料アカウント用の投稿用文章、ハッシュタグとか作成して〜っとお願いすればぱぱっと文章出してくれて、それをコピペして投稿、みたいな感じ。

1回30秒くらいで済むので十分といえば十分なんですが、やっぱり手間は手間です。

結局、有料サービスに頼らずn8n(ノーコードで自動化の仕組みを組めるツール)で自作するのが一番安上がりで自由が効くという結論になりました。

あとはXがなんか厳しくなって自動化のAPIの監視が強くアカウントが凍結される恐れもあるそう。

バンされる時についてはあまりくわしくは調べてないですがそんなこと、急にされてもという思いもあり、APIじゃなく人間が投稿すればいいのね、と。

完全自動化にすると文章チェックもないまま投稿されて、自動でバンバン投稿されてったらいずれこのアカウントは人間がさわってないと見做されて凍結される恐れもあるよなぁと、この時点でもうっすら思ってはいました。

仕組みの全体像

やりたいことをシンプルに整理すると、こんな流れです。

  1. CLAUDE君が記事を予約投稿するとき、本文と同時にカスタムフィールド(記事ごとに自由な情報を追加保存できるWordPressの機能)にX投稿文をAIに執筆してもらって保存する。ここで投稿分も事前に確認して、編集したいとこあったら編集すればあとはほんとボタン押すだけになります。
  2. WordPressが「記事が公開された」ことに気づいて、n8nに向けて「公開されたよ」という連絡を送る
  3. n8nがその連絡を受け取って、保存しておいたX投稿文を使って私のGmailに投稿文章と一緒に、ポストボタンもついて飛んでくる

要するに、記事が公開された瞬間に「せーの」でX投稿がGmaikに飛ぶ仕組みを作りたかった、ということです。予約投稿でも即時公開でも動くようにして、複数記事が溜まっていても記事ごとに区別できるので混線しない設計にしました。

シンプルに聞こえますよね。でも実際に作ってみたらつまづいたところもあり。でもこれが面白いとこでもありますよね!苦労してなんとか動くようになった経緯、ご紹介します(*´ー`*)

詰まったところ①:n8nとXを繋ぐだけで一苦労だった

まず前提として、n8nとX、WordPressをお互いに繋げる作業をするのですが、それだけで何日もかかりました。さて、無料のAPIでどこまで使えるのかを試してみます。

X APIを使うには、まずXの開発者用ページで「このアプリからアクセスします」という登録が必要でした。その登録時に、Callback URL(認証が終わったあとに戻ってくる先のURL)を外部からアクセスできる状態で用意しなくてはいけなかったんです。

自宅のNASは普段インターネットから直接アクセスできないので、Tailscale(外からでも安全にNASへアクセスできるVPNサービス)のFunnelという機能を使って、一時的に外部公開することにしました。「一旦認証が通ったら閉じればいいかな」と思ってたんですが、この「閉じればいい」が後に問題と種となってしまうのです・・・・( ゚д゚)

n8nが自分の「住所」を正しく教えてくれなかった

n8nが作ってくれるはずのWebhook URL(外部から「これが起きたら教えて」と指定するための連絡先URL)が、なぜか0.0.0.0という、どこも指してない住所みたいな値になっていました。これだと外側から呼びかけても届きません。なんでこうなるんやーーーー

原因は環境変数(アプリの動作を切り替えるための設定値)の設定不足でした。「n8nの住所はここです」と教えるN8N_HOSTN8N_PROTOCOLWEBHOOK_URLという3つの値を設定して、n8nを動かしてるDocker(アプリを箱ごと入れ替えられる仕組み)を再起動する必要がありました。

ただ、SynologyのContainer Manager(DockerのGUI版)の画面からは編集できなくて、結局コンテナを一度削除して作り直すはめになりました。

認証が「ローカルからだとダメ」だった

X APIのOAuth(違うサービス同士が、パスワードを渡さずに安全に連携するための認証の仕組み)が、ずっとUnauthorized(認証エラー)で弾かれてました。え、さっき設定したばかりなのになんで…(;´Д`A

原因は、n8nに自宅内だけで使えるアドレス(ローカルIP)でアクセスしていたことでした。認証は「誰でも見えるところからアクセスされている」ことが前提の仕組みなので、外の人から見えないローカルIPだと弾かれてしまうんです。Tailscale Funnelの外部公開URLでn8nを開いて認証する、これが正解でした。ローカルIPと外部公開URLで挙動が違うなんて、正直そこまで頭が回ってなかったです。

認証は通った、なのに投稿だけができない

長かった認証地獄をようやく抜けて、n8nに実際に投稿を作らせる部分(Create Tweetノード)を試したときのことです。返ってきたのは402(お支払いが必要です、という意味のエラー番号)でした。

そう、これが意味するのは、

有料アカウントでないとできない( ;∀;) まぁ、そりゃそうですよね、ここみんな使いたいわけですから、お弁当で言えばタコさんウインナーなわけです。

結局たどり着いた答えは、「投稿を作る」というAPI自体が無料枠に含まれておらず、最初に見た月15,000円のBasicプラン専用の機能だった、というオチでした。

認証(OAuth)や読み取りは無料でできるのに、肝心の「書き込み」だけ有料の壁の向こうにあったんです。え、ここまで来て…(;´Д`A

個人ブログのX投稿のためだけに月15,000円は、さすがに私にとってオーバーバリュー。月に100件とか投稿するならありでしょうが。。。

ここで方針転換です。n8nがXに直接投稿するのは諦めて、Create Tweetノードを丸ごと切り離しました。

代わりに使うことにしたのが、Xが標準で無料公開している「ツイート意図(intent)」というURL(https://twitter.com/intent/tweet?text=◯◯)です。このURLを開くと、指定した文章が入った状態でXの投稿画面がそのまま開きます。認証もAPIキーも一切不要。

WordPressからの連絡が届かない

n8nには「WordPressで記事が公開されたら教えて」という専用の仕組みがなかったので、WordPress側にスニペット(短いコード)を書いて、記事が公開された瞬間にn8nへ直接連絡を送る形にしました。

ConoHa(このブログを動かしている外部のレンタルサーバー)から、Tailscale Funnel経由で連絡が届くところまでは確認できました。

ここまでで、n8n・X・WordPressの3つがようやく繋がった状態になりました。でも、ここからさらに2つの壁がありました。

詰まったところ②:セキュリティ対応で肝を冷やした

繋がって安心してたら、もっと怖い話が出てきました。

n8nに重大な脆弱性(セキュリティ上の弱点。CVE-2025-68613という番号がついていて、深刻度は10段階中9.9という、ほぼ最悪ランクのもの)が見つかっていることを知ったんです。Funnelで外部公開している以上、これは絶対に他人事じゃありません。すぐにn8nを最新版に更新しました。セキュリティ関係はすぐにアップデートしましょう。

あわせて、Webhookにシークレットトークン(決まった合言葉が入っていないリクエストは無視する、簡易的な鍵のようなもの)を追加して、知らない人からの不正なリクエストを弾けるようにしました。

無料・便利だからと外部公開したまま放置するのは危険で、ここは一番緊張したポイントでした。

さらに、Synologyがアップデート後とか停電とかで再起動するとFunnelも一緒に止まってしまうことに気づいたので、タスクスケジューラー(決まった時刻・タイミングで自動実行する仕組み)に登録して、再起動後は自動でFunnelが立ち上がるようにしました。

あ、ちなみに再起動直後は少し待ってから起動させないとsynologyとネットワークがまだ不安定なので、30秒待ってから実行するようにするといいです。

ここまでで、ブログ投稿、n8n通知、Gmail送信までいったん動くには動きました。

詰まったところ③:通知だけがなぜか不安定だった

一度動き出したと思ったら、今度は「通知」まわりで立て続けに問題が出ました。

Gmail通知がある日突然止まった

Create Tweetノードを切り離した後は、このGmail送信こそが「Xに投稿する」ための本体になってたんですが、ある日突然止まりました。原因はGmail側の認証(OAuth2)の有効期限切れ。

再認証しようとしたら、これもまたFunnelが閉じていたせいでUnauthorizedになってしまい……。結局、Gmailの認証が必要な仕組みから、パスワード認証だけで送れるSMTP(メール送信の標準的な仕組み)に差し替えることで、期限切れの心配なく安定させました。

Funnelを閉じたら翌日から全滅した

「認証も終わったし、もう閉じていいか」とFunnelを閉じたら、翌日からWebhookが全部届かなくなりました。Funnelは「認証のときだけ開く」ものじゃなくて「常時ON」で運用するのが正解でした。さっきのタスクスケジューラー設定は、この教訓があって入れたものです。

Funnnelを開きっぱなしで本当に大丈夫なんだろうか。と思いますが、パスワード堅牢にしまして。まぁtailscaleそもそもいれて、同じアカウントもってないと入れないので大丈夫っちゃぁ大丈夫なんですけど。

Gmailできた通知メールの中身が空っぽだった

Gmail自体は届くのに、タイトル・投稿文・URLが全部空欄になる問題もありました。原因は、WordPressから送られてくる情報が、想定より1段階深い場所に入れ子(データの中に、さらにデータの箱が入っている状態)になっていたことでした。「ここにある」と思って指定していた場所より、もう1つ奥を見ないといけなかったっぽいです。地味な階層のズレでしたが、気づくまで結構時間がかかりました。

ここまで直して、ようやく動作確認が取れました。Xの投稿ボタン付きメールが、正しい内容で届くようになりました。ふぅ〜〜〜〜( ´ ▽ ` )

【追記】その後、予約投稿だけWebhookが届かなくなった

一度は安定したと思ってたんですが、しばらくして「予約投稿だとX自動投稿メールが届かない。でも手動で公開したときは届く」という症状に気づきました。

原因を考えていくと、WordPressのスニペットに仕込んでいた「合図」の種類が怪しいなと。WordPressには「何かが起きたらこの処理をして」と仕込む場所(フック)がいくつも用意されているんですが、自分が使っていたのは「手動で公開ボタンを押した瞬間」だけに反応する合図でした。予約投稿はWP-cron(決まった時刻になったら自動で処理を実行するWordPressの仕組み)が裏側で公開するので、この合図には引っかからなかったんです。どうりでなんか届く時と届かない時があると・・・・・( ゚д゚)

修正は、「予約中→公開に変わった瞬間」もきちんと拾える、別の種類の合図(transition_post_status)に差し替えることでした。

add_action('transition_post_status', function($new_status, $old_status, $post) {
    if ($new_status === 'publish' && $old_status !== 'publish' && $post->post_type === 'post') {
        $x_post_text = get_post_meta($post->ID, 'x_post_text', true);
        if (empty($x_post_text)) return;
        $secret = '(シークレット)';
        $body = json_encode([
            'post_id'     => $post->ID,
            'title'       => $post->post_title,
            'url'         => get_permalink($post->ID),
            'x_post_text' => $x_post_text,
        ]);
        wp_remote_post('(TailscaleのURL)', [
            'body'    => $body,
            'headers' => ['Content-Type' => 'application/json', 'X-Webhook-Secret' => $secret],
            'timeout' => 10,
        ]);
    }
}, 10, 3);

もしこれでも直らなかったら、n8n側で毎朝WordPress APIに「今日公開された記事ある?」と聞きに行く方式(ポーリング。一定間隔で繰り返し様子を見に行くやり方)に切り替える案も考えていました。Funnelのタイミングに依存しない分、こっちの方が安定するかもしれません。

感想

正直、ここまで沼るとは思ってませんでした。Gmailの通知が届くまでは順調だったのに、そこから先はTailscale・認証・通知の階層構造・WordPressの合図の違いと、詰まりポイントが一つ終わるとまた次が出てくる感じで。

結局、n8nがXに直接投稿する完全自動は実現できませんでしたが、記事を予約投稿・手動公開するだけで、Xの投稿ボタン付きメールがすぐ届くようになり、ボタンを押して中身を確認し、ポストを押すだけの状態になっています。

最後の一手間は残ったものの、コピペで消耗していた頃と比べたら十分すぎるくらいラクになりました。同士の皆さん、Webhookまわりで詰まってる人がいたら、認証はローカルIPじゃなくて外部公開URLから、というところだけでも参考になれば嬉しいです。

※本リンクはアフィリエイト広告です(PR)

🖥️ n8nをVPSで動かしたい人へ

スーツマンはSynology NASでn8nをセルフホストしていますが、NASがない方にはConoHa VPSが選択肢になると思います。n8n用のスタートアップスクリプトが用意されていて、サーバー作成と同時に環境が整うのはかなり便利だと思います。

記事が公開されたらAIが書いた文章を飛んできたGmailで確認し承認するだけでFacebookに投稿される仕組みをn8nで作った話

その後、この仕組みにFacebook投稿の自動生成も乗せることになるんですが、それはまた別の沼だったので上のリンクの記事で書いてます。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です