ランディングページを静的 HTML や Astro などで作る場合、本文やレイアウトは比較的シンプルに管理できます。
しかし、問い合わせフォームだけは少し事情が違います。
フォームは見た目だけなら、HTML の form、input、textarea、button で作れます。けれど実際には、入力内容の検証、送信処理、メール通知、スパム対策、個人情報管理、障害対応まで関わります。
つまりフォームは、単なる HTML 部品ではありません。
小さな問い合わせ受付システムです。
この記事では、ランディングページのフォーム管理について、次の観点から整理します。
- 売り切り LP なら外部フォームでよいのか
- iframe 埋め込みはどこまで使えるのか
- JSON Schema でフォーム仕様を管理できるのか
- フロントエンドだけのバリデーションで十分なのか
- 複数案件では自作のフォーム基盤を持つべきなのか
対象読者
この記事は、静的 HTML、Astro、WordPress テーマ、または小規模なフロントエンド制作で、ランディングページを作る人を想定しています。
特に、次のような人に向けています。
- ランディングページに問い合わせフォームを設置したい人
- Google フォームなどの外部サービスを使うべきか、自作すべきか迷っている人
- iframe で外部フォームを埋め込む構成を検討している人
- フォームの見た目、バリデーション、送信処理、保守責任をどう分けるか整理したい人
- 複数の LP 案件を扱う中で、フォーム管理を共通化できないか考えている人
- JSON Schema を使ってフォーム項目やバリデーションルールを管理する発想に興味がある人
この記事では、JSON Schema や CSRF 対策の細かい実装方法を深掘りするよりも、ランディングページ制作においてフォームをどのように位置づけるべきかを整理します。
そのため、主なテーマは「フォームをどう書くか」ではなく、「フォームの責任を誰が持ち、どの範囲を外部サービス・自作基盤・顧客管理に分けるか」です。
LPのフォームはHTML部品ではなく運用機能である
ランディングページのフォームは、画面上では小さく見えます。
たとえば、次のようなフォームです。
<form method="post" action="/contact">
<label>
お名前
<input type="text" name="name" required>
</label>
<label>
メールアドレス
<input type="email" name="email" required>
</label>
<label>
お問い合わせ内容
<textarea name="message" required></textarea>
</label>
<button type="submit">送信する</button>
</form>
HTML だけを見ると簡単そうです。
しかし、実際に問い合わせを受け付けるには、次のような処理が必要になります。
- 入力値の検証
- CSRF 対策
- スパム対策
- rate limit
- メール通知
- 送信ログ
- 個人情報の管理
- 完了画面
- エラー表示
- 通知先変更
- 障害時の調査
- サーバー保守
ここで重要なのは、フォームの「見た目」と「受付処理」は別物だということです。
LP 側の責任は、主に表示と導線です。
- フォームの配置
- 見出しや説明文
- レスポンシブ対応
- ボタンまでの導線
- 送信前後の体験
一方、フォーム処理側の責任は、受付と運用です。
- 送信内容の検証
- 不正送信の拒否
- スパム対策
- メール通知
- 送信ログ
- セキュリティ
- 個人情報の取り扱い
この責任を分けずに「フォームくらい作ればよい」と考えると、納品後の保守で困りやすくなります。
売り切りLPならGoogleフォームなど外部サービスが合理的
個別のランディングページを、保守契約なしで売り切る場合は、Google フォームなどの外部サービスに任せるのが合理的です。
理由は、フォームには継続的な運用責任が発生するからです。
たとえば納品後に、次のような問い合わせが来る可能性があります。
- フォームが届いていない
- スパムが増えた
- 通知先メールアドレスを変更したい
- 送信ログを確認したい
- 個人情報はどこに保存されているのか
- 外部サービスの仕様変更で表示が崩れた
保守契約がないのに自作フォームを提供すると、売り切りのはずが実質的に保守責任を引き受けることになります。
そのため、売り切り LP では次の分担が自然です。
LP本体:
制作側が静的HTML/CSS/JSとして納品する
フォーム:
Googleフォームなど外部サービスに任せる
回答管理:
顧客側のアカウントで管理する
特に重要なのは、外部フォームを使う場合でも、制作側の個人アカウントで作らないことです。
望ましいのは、顧客の Google アカウントや組織アカウントでフォームを作り、制作側が一時的に編集を手伝う形です。
よい形:
顧客アカウントでフォームを作る
制作側は一時的に編集する
納品後は顧客が回答と通知設定を管理する
避けたい形:
制作側アカウントでフォームを作る
顧客LPに埋め込む
回答データが制作側に溜まる
売り切り案件では、フォームを自作できることよりも、納品後の責任境界を明確にすることのほうが重要です。
iframe埋め込みは責任分離に向いている
外部フォームや自作フォーム基盤を LP に埋め込む場合、よく使われるのが iframe です。
<iframe
src="https://forms.example.com/f/contact"
title="お問い合わせフォーム"
class="contact-form-frame"
></iframe>
iframe を使うと、LP 側とフォーム側を分離できます。
LP側:
iframeを配置する
周囲の見出しや説明文を整える
横幅や余白を調整する
フォーム側:
フォームHTMLを生成する
CSRFトークンを発行する
送信内容を検証する
メール通知する
スパム対策する
この分離は、静的 HTML で LP を管理する場合に便利です。
ただし、iframe には制約もあります。
まず、LP 側の CSS で iframe 内部のフォームを直接スタイル変更することはできません。フォームの見た目を変えたい場合は、フォームアプリ側にテーマ設定を持たせる必要があります。
また、レスポンシブ対応では高さが課題になります。
横幅は比較的簡単です。
.contact-form-frame {
width: 100%;
border: 0;
}
しかし iframe は、内部コンテンツの高さに自動追従しません。
フォーム内でエラーメッセージが表示されたり、確認画面や完了画面に切り替わったりすると、高さが足りなくなることがあります。
自作フォーム基盤なら、フォーム側から postMessage で高さを通知する設計が考えられます。
// iframe内のフォーム側
function notifyHeight() {
window.parent.postMessage(
{
type: "formHeight",
height: document.documentElement.scrollHeight
},
"https://www.example.com"
);
}
LP 側では、それを受け取って iframe の高さを更新します。
window.addEventListener("message", (event) => {
if (event.origin !== "https://forms.example.com") {
return;
}
if (event.data?.type !== "formHeight") {
return;
}
const iframe = document.querySelector(".contact-form-frame");
iframe.style.height = `${event.data.height}px`;
});
iframe は不自由な埋め込み方法というより、LP とフォームの責任を分けるための設計手段として考えると扱いやすくなります。
JSON Schemaでフォーム仕様を管理する
複数のフォームを扱うなら、フォームの構成やバリデーションルールをコードに直書きするのではなく、JSON Schema で管理する方法が考えられます。
たとえば、問い合わせフォームの入力データを次のように定義できます。
{
"type": "object",
"required": ["name", "email", "message"],
"properties": {
"name": {
"type": "string",
"minLength": 1,
"maxLength": 80
},
"email": {
"type": "string",
"format": "email"
},
"message": {
"type": "string",
"minLength": 10,
"maxLength": 2000
}
},
"additionalProperties": false
}
このようにすると、フォームごとの差分をプログラムコードではなくスキーマとして管理できます。
ただし、JSON Schema だけでフォームのすべてを表現しようとすると無理があります。
JSON Schema が得意なのは、データ構造とバリデーションです。
- どの項目が必要か
- 文字列か数値か
- 最小文字数
- 最大文字数
- メールアドレス形式
- 選択肢
- 余計なプロパティを許可するか
一方、実際のフォームには、次のような UI 情報も必要です。
- 表示順
- ラベル
- placeholder
- 補足説明
- input か textarea か
- セクション分け
- エラー文言
- 送信ボタンの文言
- 色や余白や角丸
そのため、実務ではファイルを分けると管理しやすくなります。
form.schema.json
入力データとバリデーション
form.ui.json
表示順、部品種別、セクション構造
form.messages.ja.json
ラベル、補足文、エラー文言、完了文言
form.theme.json
色、余白、角丸、フォント、ボタンの見た目
たとえば UI 情報は次のように持てます。
{
"sections": [
{
"title": "お問い合わせ",
"fields": [
{ "name": "name", "component": "text" },
{ "name": "email", "component": "email" },
{ "name": "message", "component": "textarea", "rows": 6 }
]
}
]
}
文言は別ファイルにします。
{
"fields": {
"name": {
"label": "お名前",
"placeholder": "山田 太郎"
},
"email": {
"label": "メールアドレス",
"placeholder": "example@example.com"
},
"message": {
"label": "お問い合わせ内容"
}
},
"submit": {
"label": "送信する"
}
}
見た目もテーマとして管理します。
{
"accentColor": "#2563eb",
"borderRadius": "8px",
"fieldGap": "16px",
"buttonHeight": "48px"
}
このように分けると、エンジニアとデザイナーの分業もしやすくなります。
エンジニア:
schema
レンダラー
サーバー側検証
CSRF
送信処理
デザイナー:
theme
messages
フォーム部品の状態設計
プレビュー確認
JSON Schema は、フォーム HTML を直接置き換えるものではありません。
フォームの受け入れ条件を、機械可読な契約として管理するためのものです。
フロントエンド検証とサーバー側検証は役割が違う
JSON Schema を使うと、フロントエンドだけでもフォーム構成や入力中のバリデーションを管理できます。
たとえば、スキーマから HTML 属性を生成できます。
<input
type="text"
name="name"
required
minlength="1"
maxlength="80"
>
ブラウザ上で、送信前にエラーを表示することもできます。
フロントエンドだけでできること:
フォーム項目の生成
HTML属性の付与
入力中のエラー表示
送信前チェック
プレビュー画面
デザイナーとの確認
しかし、フロントエンドのバリデーションは最終的な防御にはなりません。
ブラウザ上の JavaScript は、利用者が無効化したり、直接 POST したり、リクエストを書き換えたりできます。
そのため、正式な検証はサーバー側で行う必要があります。
サーバー側で必要なこと:
送信データの正式検証
CSRFトークン検証
スパム対策
rate limit
保存
メール通知
ログ記録
つまり、フロントエンドとサーバー側の検証は目的が違います。
フロントエンド検証:
ユーザー体験をよくするため
サーバー側検証:
受け入れてよいデータか判断するため
理想は、同じ JSON Schema をフロントエンドとサーバー側で共有することです。
form.schema.json
↓
フロントエンド:
フォーム生成、入力中の検証、エラー表示
サーバー側:
正式検証、CSRF、スパム対策、保存、通知
これにより、バリデーションルールの二重管理を減らせます。
複数LP案件では内部ミニSaaS化を検討する
1件だけの LP なら、Google フォームなどの外部サービスで十分なことが多いです。
しかし、複数の LP を継続的に請け負う場合は、考え方が変わります。
毎回フォームを個別実装すると、次のような状態になりがちです。
LP A:
PHPでフォーム実装
LP B:
Googleフォーム埋め込み
LP C:
WordPressプラグイン
LP D:
静的HTML + 外部API
LP E:
古いjQueryフォーム
こうなると、保守時に毎回「このフォームはどう動いているのか」を調べる必要があります。
クラウドサービスを前提にすると、ここで別の選択肢が出てきます。
案件ごとにフォームを作るのではなく、
複数案件で使い回せる小さなフォーム基盤を持つ
これは、いわば内部ミニ SaaS です。
ただし、最初から Google フォームのような汎用フォーム作成サービスを目指す必要はありません。
まずは、自分たちの LP 制作で再利用するための小さなフォーム基盤で十分です。
最初に作るもの:
iframe表示
JSON Schemaによるフォーム定義
サーバー側バリデーション
CSRF
スパム対策
メール通知
送信ログ
プレビューURL
管理画面は後回しでも構いません。
最初は Git 管理の JSON ファイルでも十分です。
forms/
company-a-contact/
schema.json
ui.json
messages.ja.json
theme.json
フォーム基盤を持つと、LP 側は iframe を貼るだけになります。
<iframe
src="https://forms.example.com/f/company-a-contact"
title="お問い合わせフォーム"
></iframe>
フォームの項目変更や文言変更は、LP の HTML ではなくフォーム定義側で管理します。
これは単なる技術的な共通化ではありません。
受託制作で繰り返し発生する責任を、案件ごとにばらばらに持つのではなく、共通基盤として管理するという考え方です。
判断基準まとめ
最後に、フォーム管理の判断基準を整理します。
| 条件 | おすすめ |
|---|---|
| 保守契約なしの売り切り LP | Google フォームなど外部サービス |
| 顧客が自分で回答を管理したい | 外部フォームサービス |
| デザインを LP に強く合わせたい | 有料フォームサービスまたは自作フォーム |
| 月額保守あり | 自作フォーム基盤を検討 |
| 複数 LP を継続管理する | 内部ミニ SaaS 化を検討 |
| 送信ログや通知を一元管理したい | 自作フォーム基盤 |
| 個人情報管理の責任を持ちたくない | 外部サービスに寄せる |
単純化すると、次のように考えるとよいです。
売り切りLP:
外部サービスに任せる
保守契約あり:
自作フォーム基盤を検討する
複数案件を継続管理:
内部ミニSaaS化を検討する
まとめ
ランディングページのフォームは、単なる HTML 部品ではありません。
問い合わせを受け付けるための小さな業務機能です。
そのため、フォーム管理では「どう実装するか」より前に、「誰が運用責任を持つか」を決める必要があります。
保守契約なしの売り切り LP であれば、Google フォームなどの外部サービスに任せるのが合理的です。
一方で、複数の LP を継続的に保守するなら、JSON Schema を中心にした共通フォーム基盤を検討する価値があります。
フォームを自作するかどうかは、技術力だけで決めるものではありません。
判断基準は、契約、保守、責任範囲、複数案件への横展開です。
LP 制作におけるフォーム管理は、次のように考えると整理しやすくなります。
フォームを作る:
個別案件の実装
フォーム基盤を持つ:
複数案件を支える運用インフラ
問い合わせフォームを「とりあえず実装」する前に、それが売り切り案件なのか、保守契約込みなのか、複数案件で再利用するものなのかを確認する。
それだけで、LP 制作におけるフォーム管理の失敗はかなり減らせるはずです。