※本記事はContact Form 7 バージョン5.8以降(および検証時点の最新版)の仕様に基づきます
Headless CMSやReact、Next.jsなどからWordPressのお問い合わせフォームを送信する際、Contact Form 7(以下CF7)のREST APIは非常に強力な選択肢です。しかし、初学者が必ずと言っていいほど躓くのが「どのIDを、どこに設定すればいいのか?」という問題です。
本記事では、CF7のREST API連携で登場する「3つの異なるID」の役割と、バージョン5.8以降の重要な仕様変更について、実務ベースで明確に解説します。
IDの役割一覧
まず結論から整理します。CF7をREST APIで動かす際、以下の3つのIDの違いを理解することが成功への最短距離です。※いずれもシステム上の本質は「WordPressのPost ID(投稿ID)」ですが、対象となるデータが異なります。
| IDの種類 | 用途・送信先 | 役割 | 値の例 |
post_id(俗称: form_id) | REST APIのURLに含める | 送信するフォーム自体を特定するID | 1234 (数字) |
| ショートコードid | フロント側のHTML属性 | HTML上でフォームを一意に特定するID | cf7ab12 (英数字7文字) |
_wpcf7_container_post | POSTデータに含める | フォームが設置されている親ページのID | 5678 (数字) |
エンドポイントのpost_idはどこにあるのか?
REST APIを利用してフォームを送信するためのエンドポイントURLは以下の形式です。
https://{ backend-wordpress.com }/wp-json/contact-form-7/v1/contact-forms/{ post_id }/feedback
この{ post_id }は、「お問い合わせフォーム自体のID」です。WordPressのデータベース上では、フォームも1つの投稿(カスタム投稿タイプ wpcf7_contact_form)として扱われます。
※補足:技術記事によっては、通常の投稿のIDと区別するために、この部分を便宜上 form_id と呼ぶことがあります。用語が違っても指しているものは同じ「フォーム自体のPost ID」です。
確認方法
WordPress管理画面 > お問い合わせ > コンタクトフォームから該当のフォーム編集画面を開きます。ブラウザのアドレスバーのURLを確認してください。

https://backend-wordpress.com/wp-admin/admin.php?page=wpcf7&post=1234&action=edit
.../wp-admin/admin.php?page=wpcf7&post=1234&action=edit この post= の後ろの数字(この例では 1234)が、エンドポイントに設定する post_id です。
【よくある質問(FAQ)スキーマ】
Q. Contact Form 7 REST APIの post_id はどこで確認できますか?
A. WordPress管理画面の「コンタクトフォーム」編集画面を開き、ブラウザのアドレスバーにあるURL(post=1234 の数字部分)で確認できます。ショートコードに記載されているハッシュ文字列(v5.8以降)ではありません。
ショートコードのid
WordPressの通常運用(ショートコードを固定ページに貼り付ける方法)において、以下のようなショートコードを使用します。
[contact-form-7 id="cf7ab12" title="コンタクトフォーム 1"]
このidは、ブラウザ側(HTML)でフォームを正しく認識し、JavaScriptやCSSを適用するための識別子として機能します。

REST APIを利用して自前でフォームのUIを構築する場合、フロントエンドの開発でこのショートコードidを意識する必要は基本的にありません。REST APIのエンドポイントURLには、絶対にこのショートコードidを設定しないでください。エラーになります。
【重要】CF7 v5.8以降のショートコードIDのハッシュ化
REST APIエンドポイントに使うpost_idとショートコードのidは同じ数値ではありません。
Contact Form 7 バージョン5.8以降(WordPress バージョン6.2以上)から、ショートコードのidは「数字」から「SHA-1ハッシュから生成された7文字の識別子(英数字)」という新しい仕組みが導入されました。post_idとショートコードのidが「同じ数値」と書いている技術記事はバージョン5.8より前の情報です。
バージョン5.8以前:エラー: コンタクトフォームが見つかりません。
(post_idと同じ数字だった)バージョン5.8以降:エラー: コンタクトフォームが見つかりません。
(ハッシュ化された英数字)
【なぜ変更されたのか?】
ブロックやショートコードのポータビリティ改善と、1つのページに同じお問い合わせフォームを複数配置した際のHTML属性値(DOM id)の重複によるバグを防ぐためです。
【開発時の注意点】
古い技術記事では、「エンドポイントの {post_id} には、ショートコードに書かれている id の数字を入れます」と解説されていることが多々あります。 しかし、現在は仕様が異なります。ショートコードのid(ハッシュ)と、エンドポイントの post_id(数字)は全くの別物です。 REST APIエンドポイントには、引き続き管理画面URLから取得した「数字の post_id」を使用してください。
_wpcf7_container_post(ページID)
REST APIでお問い合わせフォームの入力データを送信する際、リクエストのBody(FormData)にはユーザーの入力値(名前、メールアドレス、メッセージなど)を含めます。 この時、必須ではありませんが、一緒に送信することを推奨するパラメータが _wpcf7_container_post です。これは一言でいうと、「このフォームが設置されているWordPressページのID」です。
これは一言でいうと、「このフォームが設置されているWordPressページのID」です。
【なぜ必要なのか?】
CF7には、メール本文で使える特殊タグがあります。
| タグ | 内容 |
| [_post_title] | フォームが設置されているページのタイトル |
| [_post_url] | フォームが設置されているページのURL |
たとえばCF7のメール設定でこのように書いているケースは よくあります:
件名:【お問い合わせ】[_post_title] よりご連絡がありました
本文:送信元ページ:[_post_url]
通常のWordPress環境(フロントとバックエンドが一体)では、 CF7が「どのページから送られてきたか」を示す値を、自動でhiddenフィールドとして出力しています。しかしHeadless環境(Next.jsなど)からREST APIで送信する場合は、フォームHTMLを出力しないため、このような処理自体が動作しません。_wpcf7_container_post がそもそも存在しない状態でPOSTされます。CF7のサーバー側は未送信フィールドを0と同等に扱う挙動になります。0、または省略して送った場合、CF7は「どのページから送信されたか」を特定できず、これらの投稿関連の特別なメールタグが空欄になります。
✖ _wpcf7_container_postを省略・0で送った場合:
→ CF7「どのページから来た送信かわからない」
→ [_post_title] →(空欄)
→ [_post_url] →(空欄)
→ 投稿関連の特別なメールタグのみ空欄になる
※ユーザーが入力した名前・メールなどは正常に届きます
✅ _wpcf7_container_postを正しく送った場合
→ CF7「ページID 5678 から来た送信だな」
→ [_post_title] →「お問い合わせ」
→ [_post_url] →「https://backend-wordpress.com/contact」
→ メールが正しく組み立てられる!
⚠️注意点
_wpcf7_container_postを省略、または0で送っても、フォーム送信自体は
処理され、成功レスポンス("mail_sent")が返る場合があります。
そのため「送信できているのに、なぜかメールの[_post_title]や[_post_url]
が空欄になる」という症状で初めて気づくケースがほとんどです。
【確認方法】
WordPress管理画面 > 固定ページ(または投稿)から、フォームの送信元として使いたいページの編集画面を開きます。ブラウザのアドレスバーのURLを確認してください。
.../wp-admin/post.php?post=5678&action=edit この post= の後ろの数字(この例では 5678)が、_wpcf7_container_post に設定するIDです。
REST APIエンドポイントに使うpost_idと_wpcf7_container_postのidは同じ数値ではありません。
このIDはフォーム自体のpost_id(REST APIエンドポイントに使う番号)とは別の値です。「フォームのID」ではなく「フォームを設置しているページのID」である点に注意してください。
_wpcf7_unit_tag について
CF7がページ上でフォームを描画するたびに自動生成するフォームインスタンスの識別子です。1ページに複数フォームを配置しても区別できるように設計されています。
【重要】CF7 バージョン 5.8.7以降、_wpcf7_unit_tagは必須フィールドになりました。
CF7 バージョン 5.8.7以降、フォーム送信の際、正しいユニットタグを含めないと400 Bad Requestを返すようになりました。
_wpcf7_unit_tag = wpcf7-f{post_id}-p{ページID}-o{インスタンス番号}
// 生成例
_wpcf7_unit_tag = wpcf7-f1234-p5678-o1
f1234 = エンドポイントで使われるpost_id
p5678 = _wpcf7_container_postのページID
o1 = 同一ページリクエスト内での描画番号(フォームが1個なら常にo1)
// 実装例
formData.append("_wpcf7_unit_tag", "wpcf7-f1234-p5678-o1");
【具体例】
| 状況 | ユニットタグ |
| お問い合わせページにフォームを1個設置した | wpcf7-f1234-p5678-o1 |
| お問い合わせページに同じフォームを2個設置した | wpcf7-f1234-p5678-o1 と wpcf7-f1234-p5678-o2 |
| TOPページにフォームを1個設置した | wpcf7-f1234-p9999-o1(ページIDが変わる、インスタンスはo1に戻る) |
| TOPページとお問い合わせページにフォームを設置した | wpcf7-f1234-p9999-o1 と wpcf7-f1234-p5678-o1(ページが別なのでそれぞれインスタンスは o1 から始まる) |
まとめと実践
ここまで、CF7 REST APIに関連する3つのIDの役割を解説しました。
最後に、フロントエンド(React, Next.js, Vueなど)で実装する際の実務上の実装例を紹介します。 各種IDは、ソースコードに直接書き込まず(ハードコードせず)、必ず .envファイル等の環境変数に登録して管理してください。
【理由】
開発環境(ローカル)と本番環境で、WordPress上のフォームやページのIDが異なるケースが多いためです。環境変数化しておくことで、コードを修正することなく環境ごとの切り替えが可能になります。
【実装例(Next.jsの場合)】
まず、.env.local ファイルにAPIのベースURLと、各種IDを定義します。(※Next.jsでブラウザ側から参照するため NEXT_PUBLIC_ プレフィックスをつけています)
.env.local
NEXT_PUBLIC_WP_API_URL= https://your-domain.com/wp-json
# フォーム自体のPost ID
NEXT_PUBLIC_CF7_FORM_ID=1234
# フォームを配置する親ページのPost ID
NEXT_PUBLIC_CF7_CONTAINER_PAGE_ID=5678
# フォーム送信の際、必須のユニットタグ
NEXT_PUBLIC_CF7_UNIT_TAG=wpcf7-f1234-p5678-o1
次に、これらを読み込んで実際にフォームデータを送信する非同期関数の例です。
api.ts
export async function submitContactForm(inputData: {
name: string;
email: string;
message: string;
}) {
// 1. 環境変数の読み込みとエンドポイントの構築
const apiUrl = process.env.NEXT_PUBLIC_WP_API_URL;
const formId = process.env.NEXT_PUBLIC_CF7_FORM_ID;
const containerId = process.env.NEXT_PUBLIC_CF7_CONTAINER_PAGE_ID;
const unitTag = process.env.NEXT_PUBLIC_CF7_UNIT_TAG;
// エンドポイントURL構築時にapiUrlとformId を使用
const endpoint = `${apiUrl}/contact-form-7/v1/contact-forms/${formId}/feedback`;
// 2. 送信データ(FormData)の構築
const formData = new FormData();
// ユーザー入力データ
formData.append("your-name", inputData.name);
formData.append("your-email", inputData.email);
formData.append("your-message", inputData.message);
// v5.8.7以降は必須。ない場合は400 Bad Requestになる。
formData.append("_wpcf7_unit_tag", unitTag ?? "");
// 任意。フォームが設置されているページのID
formData.append("_wpcf7_container_post", containerId);
// 3. APIへのデータ送信
try {
const response = await fetch(endpoint, {
method: "POST",
body: formData,
// CF7 REST APIはFormDataを受け取るため、Content-Typeは自動設定に任せる。
});
const result = await response.json();
if (!response.ok || result.status !== "mail_sent") {
throw new Error(result.message || "送信に失敗しました。");
}
return { success: true, message: result.message };
} catch(error) {
console.error("CF7 Submit Error", error);
return { success: false, message: "通信エラーが発生しました。" };
}
}
「どのIDを、どこに、なぜ使うのか」を理解していれば、 Headless構成特有のハマりどころも自信を持って対処できます。3つのIDの役割を整理したこの記事が、 実装の助けになれば幸いです。最後までお読みいただきありがとうございました。