TOSAKAFUNK

Loading...

スパムゼロを目指す!Next.js + reCAPTCHA v3 + Resend + Honeypotで実現する「UXを損なわない」フォーム実装術

スパムゼロを目指す!Next.js + reCAPTCHA v3 + Resend + Honeypotで実現する「UXを損なわない」フォーム実装術 のビジュアル

Next.js製サイトのお問い合わせフォーム、スパム対策は万全ですか?本記事では、UXを損なわないreCAPTCHA v3、シンプルなメール送信が可能なResend、そして古典的だが効果的なHoneypotを組み合わせ、Next.js (App Router) で「ちゃんと」機能するセキュアなフォームを構築する具体的な手順を、コード付きで徹底解説します。

  • スパム対策の課題従来のv2チェックボックスはUXを損ない、対策なしではビジネスリスクが増大する。
  • 本記事のゴールNext.js環境で、UXを維持しつつ強力なスパム防衛線(reCAPTCHA v3, Resend, Honeypot)を構築する。
  • 主な実装ステップHoneypot設置、reCAPTCHA v3導入、Resendでのメール送信、Server Actionsでの連携までを解説。

お問い合わせフォームの「スパム問題」、まだ放置していませんか?

Next.jsによる高速なWebサイト構築が主流となる中、ビジネスの窓口である「お問い合わせフォーム」の重要性は変わりません。しかし、このフォームは常にボットによるスパム(迷惑行為)の標的となっています。

フォームを設置したものの、毎日大量のスパムメールが届き、本当に重要なリード(見込み客)からのお問い合わせが埋もれてしまう…。そんな経験をお持ちの開発者やWeb担当者も多いのではないでしょうか。

従来のスパム対策(v2チェックボックス)の限界

「私はロボットではありません」でお馴染みのGoogle reCAPTCHA v2(チェックボックス式)は、長らくスパム対策のデファクトスタンダードでした。しかし、これには大きな欠点があります。

  • UXの悪化: ユーザーに「クリック」や「画像選択」という余計な操作を強いるため、フォーム送信の離脱率(CVRの低下)に繋がります。
  • 対策の陳腐化: 近年では、AIの進化によりv2チェックボックスを突破するボットも増えています。

Next.js時代の「スマートな」スパム対策とは?

現代のWeb開発、特にNext.jsのようなフレームワークを使う場合、UXを最優先に考えるべきです。ユーザーにストレスを感じさせず、水面下でボットを判定する「スマートな」対策が求められます。

本記事で提案するのは、まさにそのアプローチです。UXを一切損なわずに、複数の防衛線を張ることで、フォームのセキュリティを格段に高めます。

スパム対策の甘さが招く、深刻なビジネスリスク

「少しぐらいスパムが来ても、手動で削除すればいい」と考えるかもしれません。しかし、対策の甘さは、目に見えないところで深刻なビジネスリスクを引き起こします。

機会損失:本物の問い合わせがスパムに埋もれる

最大の損失はこれです。1日に100件のスパムが届けば、その中に紛れ込んだ「1件の貴重な商談」を見逃すリスクは飛躍的に高まります。営業担当がスパムの仕分けに疲弊し、結果として迅速な顧客対応(リードへのレスポンス)ができなくなるのです。

サーバー負荷とセキュリティ懸念

大量のボットアクセスは、無駄なサーバーリソースを消費します。また、悪意のあるスクリプトがフォームを通じて送信される(メールヘッダーインジェクションなど)可能性もゼロではなく、セキュリティ上の懸念も残ります。

「ちゃんとした」フォームとは、単にメールが送れるだけでなく、こうしたビジネスリスクを未然に防ぐ仕組みが組み込まれたフォームのことを指します。

解決策:Next.js + 3つの防衛線で「ちゃんとした」フォームを作る

そこで本記事では、Next.js (App Router) 環境を前提に、以下の3つの技術を組み合わせて「UXを損なわない」強力なスパム対策を実現します。

なぜこの3つ? 各技術の役割

1. Honeypot (ハニーポット)

役割:最も単純なボットの排除
人間には見えない(CSSで隠す)入力フィールドをフォームに設置します。人間は入力しないため、このフィールドに値が入っていたら、それはボットだと判断できます。古典的ですが、単純なボット対策に非常に有効です。

2. Google reCAPTCHA v3

役割:高度なボットの判定
v2とは異なり、ユーザーに操作を強いることなく、サイト上での行動(マウスの動き、クリックパターンなど)を分析し、「人間らしさ」をスコア(0.0〜1.0)で判定します。このスコアが低い(ボットの可能性が高い)リクエストを弾くことができます。

3. Resend

役割:確実なメール送信と開発体験の向上
モダンなメール送信APIサービスです。従来のSMTP設定の煩雑さから開発者を解放し、シンプルなAPIコールでメール送信を実現します。Next.jsとの親和性が非常に高いのも特徴です。

これらを組み合わせることで、「単純なボットはHoneypotで弾き、高度なボットはreCAPTCHA v3で弾き、人間からの(と判断された)リクエストのみをResendで処理する」という多層防御が完成します。

実装ガイド:Next.js (App Router) での構築ステップ

ここからは、Next.jsのApp RouterとServer Actionsを使い、具体的な実装手順を解説します。コードは簡潔化していますが、実際のプロジェクトに応用できるはずです。

ステップ1:環境構築とライブラリの準備

まず、必要なライブラリをインストールします。reCAPTCHA v3の実行には google-recaptcha-v3、Resendの利用には resend を使います。フォームのバリデーションには zod が便利です。


npm install resend zod google-recaptcha-v3
    

また、環境変数 (`.env.local`) に各種シークレットキーを登録しておきます。


# .env.local
RESEND_API_KEY=re_xxxxxxxxxxxx
RECAPTCHA_V3_SECRET_KEY=xxxxxxxxxxxx
NEXT_PUBLIC_RECAPTCHA_V3_SITE_KEY=xxxxxxxxxxxx
    

ステップ2:Honeypot(ハニーポット)の実装

フォームコンポーネントに、ボットを罠にかけるためのHoneypotフィールドを追加します。`position: absolute` と opacity: 0 などで、人間のユーザーには絶対に見えない・操作できないようにCSSで隠すのが一般的です。


// components/ContactForm.jsx

// ...(フォームの他の部分)

{/* Honeypot Field: 見えないようにCSSで隠す */}
<div style={{ position: 'absolute', left: '-5000px', opacity: '0' }} aria-hidden="true">
  <label htmlFor="honeypot">Bot detected</label>
  <input type="text" id="honeypot" name="honeypot" tabIndex={-1} autoComplete="off" />
</div>

// ...
    

サーバーサイド(Server Actions)では、この honeypot フィールドに値が入っていたら、即座に処理を中断します。

ステップ3:Google reCAPTCHA v3 の設定と導入

reCAPTCHA v3は、フロントエンドでトークンを取得し、バックエンド(Server Actions)でそのトークンを検証する流れになります。

まず、reCAPTCHA v3のプロバイダーでアプリケーションをラップします。(`layout.tsx` など)


// app/layout.tsx
import { GoogleReCaptchaProvider } from 'google-recaptcha-v3';

export default function RootLayout({ children }) {
  const siteKey = process.env.NEXT_PUBLIC_RECAPTCHA_V3_SITE_KEY || '';
  return (
    <html>
      <body>
        <GoogleReCaptchaProvider reCaptchaKey={siteKey}>
          {children}
        </GoogleReCaptchaProvider>
      </body>
    </html>
  );
}
    

次に、フォーム送信時にトークンを取得し、フォームデータと一緒に送信します。`useGoogleReCaptcha` フックを使います。


// components/ContactForm.jsx ('use client')
import { useGoogleReCaptcha } from 'google-recaptcha-v3';

export default function ContactForm() {
  const { executeRecaptcha } = useGoogleReCaptcha();

  const handleSubmit = async (formData) => {
    if (!executeRecaptcha) {
      console.error("reCAPTCHA not initialized");
      return;
    }

    try {
      // reCAPTCHAトークンを取得
      const token = await executeRecaptcha('contactForm');
      
      // Server Actionsにフォームデータとトークンを渡す
      const result = await sendEmailAction(formData, token);
      // ...(結果に応じた処理)
      
    } catch (error) {
      // ...(エラー処理)
    }
  };

  return (
    <form action={handleSubmit}>
      {/* ... フォーム項目 ... */}
      {/* ... Honeypotフィールド ... */}
      <button type="submit">送信</button>
    </form>
  );
}
    

ステップ4:Resend によるメール送信 API の設定

Resendを使ったメール送信ロジックは非常にシンプルです。Server Actions内で実行します。


// app/actions.ts ('use server')
import { Resend } from 'resend';

const resend = new Resend(process.env.RESEND_API_KEY);

export async function sendEmail(payload) {
  try {
    const { data, error } = await resend.emails.send({
      from: '自社サイト通知 <noreply@example.com>',
      to: ['your-support-email@example.com'], // お問い合わせの受信先
      subject: `【お問い合わせ】${payload.name}様より`,
      react: <EmailTemplate name={payload.name} email={payload.email} message={payload.message} />, // ResendはReactコンポーネントでメールテンプレートを書けます
    });

    if (error) {
      throw new Error("Email sending failed");
    }
    return { success: true, data };
  } catch (error) {
    return { success: false, error: error.message };
  }
}
    

ステップ5:Server Actions で全てを連携させる(バリデーションと実行)

最後に、Server Actions(`app/actions.ts`)で、Honeypotチェック、reCAPTCHA v3検証、Zodバリデーション、メール送信をすべて繋ぎ込みます。


// app/actions.ts ('use server')
import { z } from 'zod';
import { Resend } from 'resend';
// (sendEmail関数は上記ステップ4のものを想定)

// Zodスキーマ定義
const FormSchema = z.object({
  name: z.string().min(1, 'お名前は必須です'),
  email: z.string().email('有効なメールアドレスを入力してください'),
  message: z.string().min(10, '内容は10文字以上で入力してください'),
});

// reCAPTCHA v3 検証関数
async function verifyRecaptcha(token) {
  const secretKey = process.env.RECAPTCHA_V3_SECRET_KEY;
  const response = await fetch(`https://www.google.com/recaptcha/api/siteverify`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: `secret=${secretKey}&response=${token}`,
  });
  const data = await response.json();
  
  // スコアが低い(例: 0.5未満)または失敗した場合はボットと判断
  return data.success && data.score >= 0.5;
}


// メインのServer Action
export async function sendEmailAction(formData, recaptchaToken) {
  // 1. Honeypot チェック
  if (formData.get('honeypot')) {
    return { success: false, error: 'Bot detected' }; // ボットには失敗を悟られないよう、一般的なエラーを返しても良い
  }

  // 2. reCAPTCHA v3 検証
  const isHuman = await verifyRecaptcha(recaptchaToken);
  if (!isHuman) {
    return { success: false, error: 'reCAPTCHA verification failed' };
  }

  // 3. Zod バリデーション
  const parsed = FormSchema.safeParse({
    name: formData.get('name'),
    email: formData.get('email'),
    message: formData.get('message'),
  });

  if (!parsed.success) {
    return { success: false, error: 'Validation failed', issues: parsed.error.issues };
  }

  // 4. メールの送信(Resend実行)
  try {
    await sendEmail(parsed.data); // ステップ4で定義した関数
    return { success: true, message: 'お問い合わせありがとうございます。' };
  } catch (error) {
    return { success: false, error: 'Failed to send email' };
  }
}
    

まとめ:UXとセキュリティを両立したフォームで、信頼を勝ち取る

今回は、Next.js (App Router) 環境において、UXを損なわずに強力なスパム対策を施す方法として、HoneypotreCAPTCHA v3、そしてResendを組み合わせる手法を解説しました。

お問い合わせフォームは、お客様との最初の接点となる重要な場所です。そこにストレス(v2チェックボックス)やリスク(スパムの氾濫)があってはなりません。

本記事で紹介した実装を行うことで、ユーザーは快適に、そして運営者は安心してフォームを運用することが可能になります。ぜひ、あなたのNext.jsプロジェクトにも「ちゃんとした」フォームを実装してみてください。

Next.js開発のパートナーをお探しですか?

当社(株式会社ABC)は、Next.jsをはじめとするモダンなWeb技術を用いた高パフォーマンスなサイト構築を得意としています。

「自社サイトをNext.jsでリニューアルしたい」「セキュアなWebアプリケーション開発を依頼したい」など、Webに関するお困りごとがございましたら、お気軽にご相談ください。

専門のエンジニアが、お客様のビジネス課題に最適なソリューションをご提案します。