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.
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:
HTTP/2 405
server: cloudflare
content-length: 0Pages 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 Pages | Artinya bagi formulir |
|---|---|
URL preview berupa subdomain, seperti abc123.your-project.pages.dev | Satu 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 build | Hingga 100 aturan, 2.000 karakter per baris. Tidak berlaku untuk respons dari Pages Functions |
| Turnstile adalah produk Cloudflare | Anda bisa memakai pemeriksaan terkelola Ikiform tanpa key, atau widget sendiri dari dashboard Cloudflare |
- Pages menyajikan halaman kontak, dengan Content-Security-Policy dari file _headers Anda.
- Widget Turnstile berjalan dengan site key Anda dan menaruh token di formulir.
- Formulir mengirim field dan token ke Ikiform.
- Ikiform memverifikasi token dengan secret key Anda dan mencocokkan hostname token dengan domain yang diizinkan.
- Kiriman yang valid disimpan dan dikirim lewat email. Token yang hilang, kedaluwarsa, atau sudah dipakai mendapat 403 captcha_failed, dan halaman me-reset widget.
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:
<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:
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
fetchlalu butuhchallenge()dari SDK; lihat pemeriksaan manusia.
Key Anda atau pemeriksaan Ikiform
| Item | Pemeriksaan Ikiform | Key Turnstile Anda sendiri |
|---|---|---|
| Key yang dikelola | Tidak ada | Site key dan secret dari dashboard Cloudflare Anda |
| Perubahan pada formulir HTML | Tidak ada. Pemeriksaan berjalan di halaman ikiform.com | Tambahkan widget dan skripnya |
Versi fetch | Panggil challenge() dari SDK | Kirim cf-turnstile-response dari widget |
| Content-Security-Policy | Hanya form-action dan connect-src untuk Ikiform | Juga challenges.cloudflare.com di script-src dan frame-src |
| Analitik Turnstile | Tidak ada di akun Anda | Di 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:
/*
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.comJika 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.
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
- Push sebuah branch dan buka URL preview-nya. Kirim, lalu pastikan pesannya masuk dan tidak ditandai spam: preview adalah subdomain dari host proyek Anda.
- 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.
- Buka Submissions di dashboard. Kiriman baru ada di sana dengan setiap field yang Anda kirim.
- Cek inbox setiap penerima notifikasi. Masing-masing mendapat email berisi field, dan Reply terkirim ke pengirim formulir jika formulir punya field
email. - Tidak ada yang masuk? Ubah filter status di dashboard ke Spam. Spam mendapat jawaban sukses yang sama dengan kiriman asli.
Hal yang sering menjebak
| Masalah | Yang harus dilakukan |
|---|---|
405 dengan body kosong | Formulir 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 muncul | Content-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 kiriman | Site 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 spam | Domain halaman tidak ada di Allowed domains formulir. Tambahkan, ditambah localhost untuk uji lokal. Subdomain dari domain yang terdaftar diizinkan. |
Pertanyaan
Tidak. Function adalah salah satu cara menerima kiriman, tetapi Anda lalu menulis validasi, pengiriman email, dan penyaringan spam sendiri. Mengirim ke form backend menjaga proyek tetap sepenuhnya statis.
Tidak. URL preview Pages adalah subdomain dari your-project.pages.dev, jadi mengizinkan host itu sudah mencakup semuanya.
Bisa. Atur spam check formulir ke key Turnstile Anda sendiri lalu tempel site key dan secret-nya. Ikiform memverifikasi setiap token dan mencocokkan hostname-nya dengan domain yang diizinkan.
Formulir mengirim ke URL Pages Anda sendiri, yang menyajikan aset statis yang hanya menjawab request GET. Arahkan action formulir ke form backend, atau tambahkan Pages Function untuk path itu.
Tidak, tidak ada paket gratis. Pengujian gratis: Anda bisa mengirim 50 kiriman uji per bulan dari localhost sebelum membayar. Untuk go live, Pro mulai $6/bln untuk 500 kiriman, atau Anda bisa bayar sekali untuk 10.000 kiriman seharga $49 atau 50.000 kiriman seharga $149, dengan kredit yang tidak pernah kedaluwarsa.
Mentok di salah satu langkah, atau ingin panduan untuk situasi Anda? Email hello@ikiform.com.