WordPressをやめてサイトを静的化したい——残る「お問い合わせフォーム問題」をinqboxでシンプルに解決する

WordPressをやめてサイトを静的化したい——残る「お問い合わせフォーム問題」をinqboxでシンプルに解決する

WordPressで構築されたコーポレートサイトでは、コアファイルのアップデートやプラグインの脆弱性対応が継続的に発生します。こうした運用負担を軽減する手段として、Next.jsやVueで静的にHTMLを出力し、Cloudflare Pagesなどのホスティング環境へ移行する構成が選ばれるケースが増えています。静的サイトは配信サーバー上でスクリプトを実行しないため、セキュリティリスクを抑えやすく、CDNキャッシュによる高速な配信も得やすくなります。

しかし、静的ホスティング環境にはサーバーサイドの実行基盤が含まれていません。コーポレートサイトの移行を進める過程で直面することが多いのが、動的な処理を必要とする「お問い合わせフォーム」の配置先です。WordPressであればプラグインの導入だけで完結していた機能が、静的化によって数少ない動的な要件として残り、移行プロジェクトの設計工数を押し上げる要因になります。

静的サイトにおけるお問い合わせフォーム実装の選択肢と課題

静的サイトにお問い合わせフォームを設ける場合、主な選択肢は「バックエンドの自作」か「外部SaaSの利用」の二つに分かれます。

バックエンドを自作する場合、LaravelやSymfonyなどのPHPフレームワークでAPIサーバーを構築するか、Cloudflare Workersなどのサーバーレス環境に送信処理を実装することになります。送信先やデータ処理の自由度は高い反面、開発工数がかさむだけでなく、運用開始後もインフラの監視やライブラリの更新を続けなければなりません。フォーム1画面の受信処理のためだけにサーバー環境を保守するのは、WordPressの運用負荷から逃れるという本来の目的に照らすと、総保有コスト(TCO)の面で見合わない判断になりがちです。

もう一方の選択肢である埋め込み型のフォームSaaSを利用する場合、サーバー保守の負担は解消されます。その代わり、提供されるiframeや専用ウィジェットを読み込む形式が多く、自社サイトのデザイン規則に合わせたスタイリングが大きく制限されます。さらに、入力離脱を防ぐための細かな画面遷移やエラーハンドリングを自社側で制御しにくい点も、受託開発やコーポレートサイトの構築においては妥協しづらい要素です。

inqboxが静的サイトに適している理由

こうした課題を解消する選択肢の一つが、問い合わせフォームサービス「inqbox」です。管理画面で項目を設定し、発行される2行のタグをサイトに貼るだけで、フォームの表示から送信データの受信・保存・通知までをinqboxが引き受けます。

特徴は、フォームがiframeではなくサイトのDOMに直接描かれ、しかもCSSを一切持たない点です。生成されるHTMLにはdata-fieldやdata-type、data-required、data-invalidといった目印だけが付き、クラス名もスタイルも付与されません。見た目はすべてサイト側のCSSで決めるため、既存のデザイントークンやTailwind CSSの規則をそのまま適用でき、「1項目だけ幅を変える」「必須項目のラベルだけ色を変える」といった調整も属性セレクタで書けます。

また、自作APIでは実装工数がかかりがちな周辺機能があらかじめ用意されています。

  • 管理画面を通じた問い合わせ履歴の閲覧・対応状況の管理・社内メモ
  • メール通知
  • Webhook通知(Slackなど任意のチャットツールへの連携に利用できます)
  • Googleスプレッドシート連携
  • 送信完了後のリダイレクト先や資料ダウンロードボタンの設定

送信を受け付けるオリジンは管理画面で明示的に指定する方式のため、公開キーがHTMLに露出しても、登録外のサイトからの送信は受け付けられません。

公開後に増える営業メールを届く前に仕分ける

静的化とは別の観点ですが、お問い合わせフォームを公開した直後から確実に増えるのが、営業目的の送信です。SEO対策や制作代行の売り込みが本来の問い合わせに混ざって届き、担当者が一件ずつ中身を読んで判断する手間が積み重なっていきます。

inqboxは、受信した問い合わせの内容をAIが判定し、管理画面で設定した「歓迎する問い合わせ」と自然文のポリシーに沿って、受信箱・営業・確認待ちのいずれかに仕分けます。たとえば「ソフトウェア開発の依頼・相談・見積もりは歓迎、取材依頼や開発に関係のない営業は不要」と書いておけば、営業メールは受信箱とは別に振り分けられ、担当者には対応すべき問い合わせだけが届きます。間違った振り分けは管理画面で訂正でき、訂正内容は次回以降の判定の例として使われます。

WordPressのプラグインやreCAPTCHAが対象にしているのは機械的なスパム送信であり、人が手で送ってくる営業メールはそのまま通過してしまいます。この仕分けの有無は、移行後の運用負荷を左右する差になります。

実装イメージ:静的HTML / Next.jsにinqboxを置く

静的なHTMLで構成されたサイトでは、フォームを置きたい場所に管理画面で発行された2行を貼るだけで設置が完了します。scriptタグにasyncを付けると公開キーを読めなくなるので、そのまま貼ります。

<script src="https://inqbox.jp/widget.js" data-key="あなたの公開キー"></script>
<div data-inqbox-form></div>

フォームは<div data-inqbox-form>の中に描かれます。見た目はサイトのCSSで整えます。

[data-inqbox-form] label {
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
}

[data-inqbox-form] input,
[data-inqbox-form] textarea,
[data-inqbox-form] select {
  font: inherit;
  width: 100%;
  box-sizing: border-box;
  padding: 0.5rem 0.7rem;
  border: 1px solid #d5ddda;
  border-radius: 0.375rem;
}

/* 必須項目のラベルだけ色を変える */
[data-inqbox-form] [data-required] > label > span {
  color: #0d6f63;
}

/* 入力エラーのある項目は枠を赤くする */
[data-inqbox-form] [data-invalid] input,
[data-inqbox-form] [data-invalid] textarea {
  border-color: #b3261e;
}

Next.jsやVueでは、スクリプトが読み込まれた時点ではまだフォームの設置先が描画されていないため、描画後にwindow.inqbox.mount(要素)を呼びます。専用パッケージは不要です。

送信が成功するとwindowにinqbox:submittedイベントが飛ぶので、計測タグの発火や独自のサンクス画面への遷移はここで行います。以下はNext.js(App Router)のクライアントコンポーネントで、マウントと送信完了後の遷移までをまとめた例です。

'use client';

import { useEffect, useRef } from 'react';
import { useRouter } from 'next/navigation';

type InqboxSubmittedEvent = CustomEvent<{
  inquiryId: string;
  folder: 'inbox' | 'sales' | 'confirm';
  redirectUrl: string | null;
}>;

declare global {
  interface Window {
    inqbox?: { mount: (el: HTMLElement | null) => void };
  }
  interface WindowEventMap {
    'inqbox:submitted': InqboxSubmittedEvent;
  }
}

export default function ContactForm() {
  const ref = useRef<HTMLDivElement>(null);
  const router = useRouter();

  useEffect(() => {
    // ページを行き来して戻ってきたときは読み込み済みのものを使う
    if (window.inqbox) {
      window.inqbox.mount(ref.current);
    } else {
      const script = document.createElement('script');
      script.src = 'https://inqbox.jp/widget.js';
      script.dataset.key = 'あなたの公開キー';
      script.onload = () => window.inqbox?.mount(ref.current);
      document.head.appendChild(script);
    }

    const handleSubmitted = (e: InqboxSubmittedEvent) => {
      // 管理画面でリダイレクト先を設定している場合は widget 側の移動を止め、
      // Next.js のルーターで遷移する
      if (e.detail.redirectUrl) {
        e.preventDefault();
      }
      router.push('/contact/thanks');
    };

    window.addEventListener('inqbox:submitted', handleSubmitted);
    return () => {
      window.removeEventListener('inqbox:submitted', handleSubmitted);
    };
  }, [router]);

  return <div ref={ref} data-inqbox-form />;
}

同じ要素に対してmountを二度呼んでもフォームは1つだけ描かれるため、React の Strict Mode でuseEffectが二度走っても問題ありません。

e.detail.folderには受信箱・営業・確認待ちのどれに振り分けられたかが入るため、営業と判定された送信では計測タグを発火させない、といった制御もこのハンドラで行えます。管理画面の「送信完了後の動作」にリダイレクト先を設定しておけば、イベントを扱わなくても送信後に指定ページへ移動します。

サーバーレス化による保守運用の変化

WordPressから静的サイトへ移行し、フォームにinqboxを採用することで、WebサーバーやPHPの実行基盤、フォーム送信用APIサーバーを自前で保守する必要がなくなります。

PHPやWebサーバーのバージョンアップ作業、プラグインの更新に伴う表示崩れリスクの検証といった定期保守から解放され、サイト全体のセキュリティ耐性が高まります。自作APIのインフラ監視にかかる継続的なコストも発生しません。静的ホスティング環境の持つ表示性能や安全性を活かしつつ、問い合わせ窓口の運用負荷を抑えた構成を維持できます。

望月 涼太 / polidog

望月 涼太 / polidog

Co-Founder / Web Engineer