Formulir kontak di situs statis

Formulir kontak di situs statis5 menit baca

Menambahkan formulir kontak ke situs Cloudflare Pages

Tambahkan formulir kontak ke Cloudflare Pages tanpa menulis Function: kirim ke form backend, atur header di _headers, dan pakai key Turnstile Anda sendiri.

Preet SutharPendiri, Ikiform

Aset statis di Cloudflare Pages menjawab POST dengan 405, jadi tulis Pages Function atau kirim formulir ke form backend. Dengan Ikiform Anda tidak menulis Function: atur action formulir ke endpoint Anda, tambahkan your-project.pages.dev ke Allowed domains, yang juga mencakup setiap URL preview, dan cantumkan Ikiform di Content-Security-Policy _headers Anda. Jika Anda sudah memakai Cloudflare, Anda bisa memasang key Turnstile sendiri ke formulir.

Apa yang berbeda di Cloudflare Pages

Pages menyajikan hasil build Anda dari jaringan Cloudflare. Aset statis menjawab GET; POST ke aset mendapat 405 tanpa body:

Respons untuk POST
HTTP/2 405
server: cloudflare
content-length: 0

Pages bisa menjalankan kode, lewat Pages Functions di folder functions/. functions/contact.ts bisa menerima kiriman, tetapi berarti Anda menulis backend: parsing, validasi, penyedia email, penyaringan spam, dan penyimpanan. Dokumentasi Cloudflare sendiri juga menyarankan memulai proyek baru di Workers, bukan Pages. Mengirim ke form backend menghindari keduanya, dan formulir yang sama tetap berfungsi tanpa perubahan jika nanti Anda pindah ke static assets Workers.

Ciri PagesArtinya bagi formulir
URL preview berupa subdomain, seperti abc123.your-project.pages.devSatu domain yang diizinkan, your-project.pages.dev, mencakup production dan setiap preview. Preview Vercel dan Netlify perlu entri sendiri
Header berasal dari file _headers di hasil buildHingga 100 aturan, 2.000 karakter per baris. Tidak berlaku untuk respons dari Pages Functions
Turnstile adalah produk CloudflareAnda bisa memakai pemeriksaan terkelola Ikiform tanpa key, atau widget sendiri dari dashboard Cloudflare
Formulir kontak di Cloudflare Pages dengan key Turnstile Anda sendirihalamandipages.devdisajikandengan_headerswidgetTurnstilesite keyAndaIkiformverifikasidengansecretAndadisimpandandikirimemail403captcha_failedresetwidgettokenPOST + tokentoken salah atau terpakaiFormulir kontak di Cloudflare Pages dengan key Turnstile Anda sendirihalaman dipages.devdisajikan dengan_headerswidget Turnstilesite key AndaIkiformverifikasi dengansecret Andadisimpan dandikirim email403captcha_failedreset widgettokenPOST + tokentoken salah atau terpakai
  1. Pages menyajikan halaman kontak, dengan Content-Security-Policy dari file _headers Anda.
  2. Widget Turnstile berjalan dengan site key Anda dan menaruh token di formulir.
  3. Formulir mengirim field dan token ke Ikiform.
  4. Ikiform memverifikasi token dengan secret key Anda dan mencocokkan hostname token dengan domain yang diizinkan.
  5. Kiriman yang valid disimpan dan dikirim lewat email. Token yang hilang, kedaluwarsa, atau sudah dipakai mendapat 403 captcha_failed, dan halaman me-reset widget.
Jika memakai pemeriksaan terkelola Ikiform, tidak ada widget di halaman Anda; formulir HTML sama sekali tidak perlu diubah.

Formulirnya

Versi ini memakai widget Turnstile Anda sendiri, yang masuk akal jika Anda sudah mengelola Turnstile di akun Cloudflare. Di pengaturan formulir, atur Spam check ke Your own Turnstile keys lalu tempel site key dan secret-nya. Widget menambahkan field cf-turnstile-response ke formulir, dan select team memberi tahu siapa yang sebaiknya menjawab:

public/contact.html
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>

<form id="contact" 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">Email</label>
  <input id="email" name="email" type="email" autocomplete="email" required />

  <label for="team">Who should read this?</label>
  <select id="team" name="team">
    <option value="support">Support</option>
    <option value="sales">Sales</option>
    <option value="press">Press</option>
  </select>

  <label for="message">Message</label>
  <textarea id="message" name="message" rows="6" minlength="10" required></textarea>

  <div class="cf-turnstile" data-sitekey="0x4AAAAAAAxxxxxxxxxxxxxx"></div>
  <input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />
  <button type="submit">Send</button>
  <p id="contact-status" role="status"></p>
</form>

<script src="/contact.js" defer></script>

Skrip menjaga pengunjung tetap di halaman. Token Turnstile hanya sekali pakai, jadi setelah pengiriman gagal, skrip me-reset widget untuk percobaan berikutnya:

public/contact.js
const form = document.querySelector("#contact");
const status = document.querySelector("#contact-status");

form.addEventListener("submit", async (event) => {
  event.preventDefault();
  status.textContent = "Sending";
  const response = await fetch(form.action, {
    method: "POST",
    headers: { Accept: "application/json" },
    body: new FormData(form),
  }).catch(() => null);
  const result = await response?.json().catch(() => null);

  if (result?.ok) {
    form.replaceWith(Object.assign(document.createElement("p"), { textContent: "Sent. We'll reply by email." }));
    return;
  }

  window.turnstile?.reset();
  if (result?.error?.code === "captcha_failed") {
    status.textContent = "The human check expired. Complete it again, then send.";
    return;
  }
  const fields = result?.error?.fields ?? {};
  status.textContent = Object.values(fields)[0]?.[0] ?? result?.error?.message ?? "Couldn't send. Try again.";
});
  • Di pengaturan widget Turnstile di Cloudflare, cantumkan hostname tempat formulir berjalan. Ikiform juga mencocokkan hostname token dengan Allowed domains formulir.
  • Kiriman tanpa token valid ditolak dengan 403 captcha_failed. Termasuk kiriman HTML biasa yang dikirim sebelum widget selesai.
  • Tidak ingin key sama sekali? Atur Spam check ke Ikiform, hapus widget dan skripnya, dan kiriman HTML menjalankan pemeriksaan di ikiform.com. Versi fetch lalu butuh challenge() dari SDK; lihat pemeriksaan manusia.

Key Anda atau pemeriksaan Ikiform

ItemPemeriksaan IkiformKey Turnstile Anda sendiri
Key yang dikelolaTidak adaSite key dan secret dari dashboard Cloudflare Anda
Perubahan pada formulir HTMLTidak ada. Pemeriksaan berjalan di halaman ikiform.comTambahkan widget dan skripnya
Versi fetchPanggil challenge() dari SDKKirim cf-turnstile-response dari widget
Content-Security-PolicyHanya form-action dan connect-src untuk IkiformJuga challenges.cloudflare.com di script-src dan frame-src
Analitik TurnstileTidak ada di akun AndaDi dashboard Cloudflare Anda, di samping widget lainnya

Pemeriksaan Ikiform lebih sedikit setup-nya. Key sendiri masuk akal jika Anda sudah memantau analitik Turnstile, atau ingin tampilan dan posisi widget sepenuhnya Anda kendalikan.

Menguji widget secara lokal

Cloudflare menerbitkan site key dan secret untuk pengujian yang selalu lolos atau selalu gagal, seperti pasangan always-pass 1x00000000000000000000AA dan 1x0000000000000000000000000000000AA. Secret production menolak token yang dibuat dengan key pengujian, jadi jangan campur keduanya di satu formulir. Buat formulir Ikiform kedua untuk pengembangan lokal, beri pasangan key pengujian, dan ganti data-sitekey dan ID formulir bersamaan. Biarkan Allowed domains formulir itu kosong: Ikiform hanya memeriksa hostname token jika daftarnya diisi, dan Cloudflare tidak mendokumentasikan hostname mana yang dilaporkan token pengujian.

File _headers

Taruh _headers di direktori aset statis Anda, seperti public/, agar ikut masuk ke hasil build. Turnstile butuh script-src dan frame-src untuk https://challenges.cloudflare.com, dan Ikiform butuh form-action dan connect-src:

public/_headers
/*
  Content-Security-Policy: default-src 'self'; script-src 'self' https://challenges.cloudflare.com; frame-src https://challenges.cloudflare.com; form-action 'self' https://ikiform.com; connect-src 'self' https://ikiform.com

Jika nanti Anda menambahkan Pages Function ke proyek yang sama, responsnya tidak mendapat header ini. Atur di kode Function-nya.

Jika Anda pindah ke Workers

Memindahkan situs ke static assets Workers mengubah cara deploy, bukan cara kerja formulir. Formulir tetap mengirim ke ikiform.com, jadi tidak ada yang menanganinya di sisi Cloudflare. Cek dua hal setelah pindah: host workers.dev atau route baru ada di Allowed domains, dan header Anda masih sampai ke halaman, karena Workers punya cara sendiri untuk mengaturnya.

Tambahkan your-project.pages.dev dan domain kustom Anda di Allowed domains. Jangan pernah menambahkan pages.dev begitu saja: setiap proyek Pages akan cocok.

Pastikan berfungsi

  1. Push sebuah branch dan buka URL preview-nya. Kirim, lalu pastikan pesannya masuk dan tidak ditandai spam: preview adalah subdomain dari host proyek Anda.
  2. Kirim lagi di production, lalu tekan kembali dan kirim formulir yang sama untuk kedua kalinya tanpa menyelesaikan widget. Yang diharapkan adalah pesan reset, bukan kiriman yang tersimpan.
  3. Buka Submissions di dashboard. Kiriman baru ada di sana dengan setiap field yang Anda kirim.
  4. Cek inbox setiap penerima notifikasi. Masing-masing mendapat email berisi field, dan Reply terkirim ke pengirim formulir jika formulir punya field email.
  5. Tidak ada yang masuk? Ubah filter status di dashboard ke Spam. Spam mendapat jawaban sukses yang sama dengan kiriman asli.

Hal yang sering menjebak

MasalahYang harus dilakukan
405 dengan body kosongFormulir mengirim ke URL Pages. Cek action yang di-deploy, dan pastikan tidak ada Function atau aturan redirect yang menulis ulang /contact.
Widget Turnstile tidak pernah munculContent-Security-Policy memblokir challenges.cloudflare.com. Tambahkan ke script-src dan frame-src, dan cek _headers ada di hasil build.
403 captcha_failed di setiap kirimanSite key dan secret berasal dari widget yang berbeda, hostname tidak tercantum di widget, atau secret pengujian dipasangkan dengan site key sungguhan.
403 origin_not_allowed, atau setiap kiriman masuk ke spamDomain halaman tidak ada di Allowed domains formulir. Tambahkan, ditambah localhost untuk uji lokal. Subdomain dari domain yang terdaftar diizinkan.

Pertanyaan

Mentok di salah satu langkah, atau ingin panduan untuk situasi Anda? Email hello@ikiform.com.