静的サイトのお問い合わせフォーム

静的サイトのお問い合わせフォーム2 分で読めます

Amazon S3 の静的ウェブサイトにお問い合わせフォームを追加する

S3 のウェブサイトエンドポイントは平文 HTTP の GET と HEAD しか受け付けません。お問い合わせフォームはフォームバックエンドに送信し、リダイレクトは CloudFront で正しく扱います。

Preet SutharIkiform 創業者

S3 のウェブサイトエンドポイントはフォームの POST に 405 MethodNotAllowed を返すので、フォームの action は Ikiform のエンドポイントに向けます。エンドポイントは平文の http: なので、そこへ戻る _redirect はたどられません。フォーム設定の Redirect after submit を設定するか、https: のために CloudFront を前段に置いてください。バケットの REST エンドポイントの前に CloudFront を置く場合、デフォルトルートオブジェクトはルートにしか適用されないので、thanks.html のような完全なファイル名にリダイレクトします。

S3 ならではの違い

静的ウェブサイトホスティングを設定したバケットには、http://www.example.com.s3-website-us-east-1.amazonaws.com のような専用のウェブサイトエンドポイントが付きます。AWS のドキュメントによると、オブジェクトに対しては GET と HEAD リクエストしかサポートしていません。フォームを送信すると HTML のエラーページが返ります:

POST への応答
<html>
<head><title>405 Method Not Allowed</title></head>
<body>
<h1>405 Method Not Allowed</h1>
<ul>
<li>Code: MethodNotAllowed</li>
<li>Message: The specified method is not allowed against this resource.</li>
<li>Method: POST</li>
<li>ResourceType: OBJECT</li>

お問い合わせフォームに影響する S3 の特徴があと 3 つあります:

S3 の特徴フォームへの影響
ウェブサイトエンドポイントは HTTPS をサポートしないIkiform は https: の URL への _redirect しかたどらないので、素のエンドポイントへ戻るリダイレクトは無視される
レスポンスヘッダーはバケットではなく CloudFront が付けるContent-Security-Policy は CloudFront のレスポンスヘッダーポリシーで設定する
CloudFront のデフォルトルートオブジェクトはルートにしか適用されないREST オリジンでは https://example.com/thanks/ で thanks/index.html は読み込まれない。thanks.html にリダイレクトする

https: のサイトには、CloudFront 上で動く Amplify Hosting か、バケットの前に CloudFront を置く構成を AWS は推奨しています。フォームはどちらでも動きます。変わるのは、送信後に訪問者がたどり着く場所です。

CloudFront の有無による、送信後の訪問者の行き先素の S3 ウェブサイトエンドポイント、httpCloudFront と独自ドメイン、httpshttp://…s3-website…contact.htmlIkiform保存してメール通知Ikiform のサンクスページhttp への _redirect は無視https://www.example.comcontact.htmlIkiform保存してメール通知自分の thanks.html_redirect に従うPOST303POST303CloudFront の有無による、送信後の訪問者の行き先素の S3 ウェブサイトエンドポイント、httpCloudFront と独自ドメイン、httpshttp://…s3-website…contact.htmlIkiform保存してメール通知Ikiform のサンクスページhttp への _redirect は無視https://www.example.comcontact.htmlIkiform保存してメール通知自分の thanks.html_redirect に従うPOST303POST303
  1. 素のウェブサイトエンドポイントでは、ページは http で配信され、Ikiform に送信します。
  2. Ikiform はメッセージを保存しますが、http のアドレスへの _redirect は無視し、ホストされたサンクスページを表示します。
  3. ダッシュボードで Redirect after submit を設定すれば回避できます。こちらは http の URL も受け付けるからです。
  4. 独自ドメインで CloudFront の背後に置けば、ページは https になります。
  5. ドメインが Allowed domains にあれば、Ikiform は _redirect に従って thanks.html に移動させます。
どちらの場合もメッセージは保存されます。https かどうかで変わるのは、ブラウザーの行き先という最後の段階だけです。

どの CloudFront オリジンを使うか

CloudFront がバケットを読む方法は 2 つあり、どちらを選ぶかでサンクスページの URL の形が決まります:

オリジンバケットフォルダーのインデックスページサンクスページの URL
S3 ウェブサイトエンドポイントをカスタムオリジンとして公開読み取りが必要使える。/thanks/ で thanks/index.html が読み込まれる/thanks/ または /thanks.html
REST エンドポイントとオリジンアクセスコントロール非公開のまま、CloudFront だけが読み取るルートのみ。デフォルトルートオブジェクトで/thanks.html のように完全なファイル名

完全なファイル名ならどちらでも使えるので、サンプルではそうしています。CloudFront を自分で運用したくなければ、Amplify Hosting で https: とマネージド CDN 付きでバケットからデプロイできます。その場合もフォームの変更は不要です。

フォーム

S3 のサイトは企業サイトであることが多いので、このフォームでは仕事用のメールアドレスと会社名を尋ねます。thanks.html と同じ場所に contact.html としてアップロードします:

contact.html
<form action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post">
  <label for="name">Name</label>
  <input id="name" name="name" autocomplete="name" required />

  <label for="email">Work email</label>
  <input id="email" name="email" type="email" autocomplete="email" required />

  <label for="company">Company</label>
  <input id="company" name="company" autocomplete="organization" />

  <label for="message">How can we help?</label>
  <textarea id="message" name="message" rows="6" minlength="10" required></textarea>

  <input type="hidden" name="_redirect" value="https://www.example.com/thanks.html" />
  <input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />
  <button type="submit">Send</button>
</form>
  • _redirect はファイル名付きで CloudFront のドメインを指しています。www.example.com か example.com が Allowed domains にある場合にだけ従います。
  • 素の http: エンドポイントでは、_redirect の入力欄を削除し、代わりに Redirect after submit をサンクスページに設定します。この設定は自分のチームしか変更できないので、http: の URL も受け付けます。
  • フィールド名は email のままにしてください。通知メールはこれを reply-to アドレスとして使います。

企業サイトでは、問い合わせの受信トレイを複数人で読むのが普通です。確認済みの通知先をフォームに最大 10 件まで追加すると、全員にメッセージが届き、Reply を押した人が送信者に直接返信できます。共有チャンネルやチケットのキューにも届けたい場合は、署名付き Webhook を追加し、Zapier や自分のコードで振り分けてください。company フィールドはプレーンテキストで届くので、どちらでもそのまま絞り込みに使えます。

アップロードの前に、localhost を Allowed domains に入れてローカルサーバーでページをテストします。localhost からの送信はテストとして扱われるので、バケットで配信する前にフォームを仕上げられます。

フォルダーをバケットに同期し、CloudFront でページを無効化して、新しいフォームがすぐに配信されるようにします:

ターミナル
aws s3 sync ./site s3://www.example.com --delete

aws cloudfront create-invalidation --distribution-id E1EXAMPLE --paths "/contact.html" "/thanks.html"

AWS のホストでの許可ドメイン

ページの配信元追加するもの
素のウェブサイトエンドポイントwww.example.com.s3-website-us-east-1.amazonaws.com のような、バケットのエンドポイントのホスト全体
CloudFront のデフォルトドメインテスト中は d111111abcdef8.cloudfront.net のホストそのもの
独自ドメインexample.com。www.example.com もカバーする

amazonaws.com、リージョンごとの s3-website のサフィックス、cloudfront.net だけを追加してはいけません。世界中のすべてのバケットやディストリビューションが一致してしまいます。AWS が S3 ウェブサイトエンドポイントのドメインを Public Suffix List に登録しているのも同じ理由で、バケットはそれぞれ別のサイトだからです。

CloudFront でヘッダーを付ける

S3 のオブジェクトメタデータでは Content-Security-Policy を設定できないので、CloudFront のレスポンスヘッダーポリシーで設定し、ディストリビューションのデフォルトキャッシュ動作に関連付けます。ヘッダーの値は 1,783 文字までです:

csp-policy.json
{
  "Name": "site-csp",
  "SecurityHeadersConfig": {
    "ContentSecurityPolicy": {
      "Override": true,
      "ContentSecurityPolicy": "default-src 'self'; form-action 'self' https://ikiform.com; connect-src 'self' https://ikiform.com"
    }
  }
}
ターミナル
aws cloudfront create-response-headers-policy --response-headers-policy-config file://csp-policy.json
Override: true は、オリジンが送る CSP を置き換えます。素の S3 オリジンには CSP はありませんが、後でオリジンを変えてもこのポリシーが優先されるようになります。

動作を確認する

  1. 上の表のドメインと、テスト中は localhost を追加します。
  2. CloudFront のドメインから送信します。独自ドメインの thanks.html に移動するはずです。
  3. 素のウェブサイトエンドポイントも公開したままにする場合は、そこから送信し、Redirect after submit で設定したページに移動することを確認します。
  4. ダッシュボードで Submissions を開きます。送ったすべてのフィールドを含む新しい送信データがあります。
  5. 通知の受信者それぞれの受信トレイを確認します。各自にフィールド入りのメールが届き、フォームに email フィールドがあれば Reply の宛先は送信者になります。
  6. 何も届かない場合は、ダッシュボードのステータスフィルターを Spam に切り替えてください。スパムにも本物の送信と同じ成功の応答が返ります。

ハマりどころ

問題対処
Code: MethodNotAllowed 付きの 405 Method Not Allowedフォームがバケットに送信されています。CloudFront がまだ古い contact.html を配信しているかもしれないので、無効化してください。
リダイレクト後に AccessDenied や 403 になるREST オリジンの CloudFront ディストリビューションで、リダイレクト先が /thanks/ のようなフォルダーになっています。thanks.html を指定してください。
自分のページではなく Ikiform のサンクスページに移動する_redirect は、ドメインが Allowed domains にあり、かつ https: の URL でない限り無視されます。ドメインを追加するか、代わりにフォーム設定で Redirect after submit を設定してください。
403 origin_not_allowed が返るか、すべての送信がスパムになるページのドメインがフォームの Allowed domains にありません。ドメインと、ローカルのテスト用に localhost を追加してください。登録済みドメインのサブドメインは許可されます。

よくある質問

手順で詰まったときや、あなたの状況に合うガイドがほしいときはメールでどうぞ: hello@ikiform.com