가이드

가이드12분 분량

문의 폼 스팸을 막는 방법

허니팟, 시간 검사, 콘텐츠 점수, 레이트 리밋, 필요한 곳에만 사람 확인까지, 실제 방문자에게 부담 없는 여러 겹의 검사로 문의 폼 스팸을 막으세요.

Preet SutharIkiform 창업자

마찰을 더하기 전에 비용이 적고 눈에 보이지 않는 검사부터 쌓으세요. 숨겨진 허니팟 필드, 폼 작성 최소 시간, 링크와 스팸 문구에 대한 점수, 방문자별 레이트 리밋입니다. Cloudflare Turnstile 같은 사람 확인은 그래도 스팸이 통과할 때만 추가하고, 토큰은 서버에서 검증하세요. 걸러 낸 것은 버리지 말고 저장해 두면 가끔 섞이는 실제 메시지를 되살릴 수 있습니다.

공개 페이지의 문의 폼은 검색 엔진에 색인된 지 며칠 만에 쓰레기 제출을 받기 시작합니다. 대부분은 여러분을 노린 것이 아닙니다. 스크립트가 웹을 돌며 아무 <form>이나 찾아 모든 입력을 채우고 POST하면서 링크가 어딘가에 걸리기를 바랍니다. 그보다 적은 일부는 소프트웨어가 조종하는 실제 브라우저에서 오고, 또 일부는 같은 홍보 문구를 수천 개의 폼에 붙여 넣는 대가로 돈을 받는 사람에게서 옵니다. 종류마다 막는 검사가 다르기 때문에 한 가지 요령으로는 해결되지 않습니다.

이 가이드는 각 방어 수단이 무엇을 잡고 실제 방문자에게 어떤 부담을 주는지, 어떤 스택에서든 실행할 수 있는 코드와 함께 설명합니다. 그런 다음 폼 백엔드인 Ikiform이 같은 계층을 어떻게 대신 실행하는지 보여 줍니다.

비용이 낮은 순서로 쌓는 계층

검사는 비용 순으로 배치하세요. 요청 수를 세는 일은 거의 공짜이므로 본문을 읽기 전에 실행합니다. 캡차처럼 다른 서비스를 호출하는 검사는 저렴한 검사를 통과한 뒤에 실행합니다. 콘텐츠 점수는 파싱된 필드가 필요하므로 마지막입니다. Ikiform은 모든 POST에 이 순서로 검사를 실행합니다.

POST가 도착해서 저장되거나 스팸이 되기까지 스팸 검사가 실행되는 순서즉시 거부점수를 매긴 뒤 어느 쪽이든 저장POST 도착페이지, 스크립트,서버에서레이트 리밋한 방문자가 너무 많이:429허용 도메인다른 사이트에서 보냈나?사람 확인켜져 있으면Turnstile 토큰허니팟채워지면: +5시간과 내용너무 빠름, 링크, 문구,반복5점 이상?받음이메일과 웹훅 발송스팸같은 성공 응답, 발송없음횟수출처토큰아니요예POST가 도착해서 저장되거나 스팸이 되기까지 스팸 검사가 실행되는 순서즉시 거부점수를 매긴 뒤 어느 쪽이든 저장POST 도착페이지, 스크립트, 서버에서레이트 리밋한 방문자가 너무 많이: 429허용 도메인다른 사이트에서 보냈나?사람 확인켜져 있으면 Turnstile 토큰허니팟채워지면: +5시간과 내용너무 빠름, 링크, 문구, 반복5점 이상?받음이메일과 웹훅 발송스팸같은 성공 응답,발송 없음횟수출처토큰아니요예
  1. POST가 폼 엔드포인트에 도착합니다.
  2. 레이트 리밋이 먼저 방문자별 POST 수를 셉니다. 폭주하는 요청은 본문을 읽기 전에 429로 거부됩니다.
  3. 허용 도메인은 POST가 온 페이지를 여러분이 등록한 도메인과 비교합니다.
  4. 사람 확인이 켜져 있으면 POST에 유효한 Turnstile 토큰이 있어야 합니다.
  5. 허니팟이 채워져 있으면 점수에 5를 더합니다.
  6. 시간, 링크, 알려진 스팸 문구, 일회용 주소, 반복 제출은 더 작은 점수를 더합니다.
  7. 5점 이상이면 POST는 스팸으로 저장됩니다. 그보다 낮으면 POST를 받고 이메일과 웹훅이 나갑니다.
윗줄은 POST를 거부합니다. 아랫줄은 절대 거부하지 않고 점수만 매기므로, 오탐은 잃어버린 메시지가 아니라 클릭 한 번으로 복원할 수 있는 항목이 됩니다.

방어 수단 비교

모든 방어 수단에는 대가가 있습니다. 이 표는 각 수단이 놓치는 것과, 여러분이 연락받고 싶은 사람들에게 요구하는 것을 솔직하게 보여 줍니다.

방어 수단막는 것놓치는 것실제 방문자의 부담
Honeypot모든 입력을 채우는 스크립트CSS를 읽거나 숨겨진 필드를 건너뛰는 봇, 그리고 사람스크린 리더와 자동 완성에서 숨겨져 있다면 없음
시간 함정누구도 그렇게 빨리 입력할 수 없는 속도로 보낸 POST느린 봇과 기다리는 봇최소 시간이 몇 초 정도라면 없음
링크 수, 스팸 문구, Akismet 같은 콘텐츠 점수SEO 홍보, 링크 뿌리기, 카지노와 약 광고 스팸깔끔해 보이는 스팸. 링크가 있는 실제 메시지를 잘못 표시할 수 있음걸러 낸 것을 검토한다면 없음
Rate limiting폭주와 반복 실행되는 스크립트느리게 분산된 스팸하나의 IP를 쓰는 사무실이나 행사장은 엄격한 제한에 걸릴 수 있음
허용 도메인다른 곳에서 엔드포인트로 바로 보낸 POSTOrigin 헤더를 위조하는 스크립트없음
Cloudflare Turnstile대부분의 자동화된 브라우저돈을 받는 사람 스패머보통은 보이지 않음. 가끔 클릭이 필요하고 스크립트가 필요함
reCAPTCHA 또는 hCaptcha대부분의 자동화된 브라우저돈을 받는 사람 스패머와 캡차 풀이 서비스일부 방문자에게 이미지 퍼즐, 그리고 서드파티 스크립트

처음 다섯 가지부터 시작하세요. 눈에 보이지 않으면서 작은 사이트에 오는 스팸의 대부분을 잡습니다. 사람 확인은 그 전이 아니라, 대시보드에서 스팸이 여전히 통과한다는 것이 보일 때 추가하세요. 모든 방문자가 부담을 느낄 수 있는 유일한 계층이기 때문입니다. 캡차 없이 스팸 막기 페이지에서 보이지 않는 계층만으로 어디까지 막을 수 있는지 볼 수 있습니다.

스팸과 싸우기 전에 스팸을 읽기

해결책을 고르기 전에 스팸 메시지 열 개를 살펴보세요. 공통점을 보면 누가 보내는지 알 수 있고, 그것이 막아 줄 계층을 알려 줍니다.

스팸의 모습에서 그것을 막는 계층까지모든 필드에 무작위 텍스트스크립트 봇HTML을 파싱해 모든 입력을 채움허니팟한 출처에서 분당 수십 건반복 실행되는 스크립트레이트 리밋로드 후 1초 안의 깔끔한POST헤드리스 브라우저시간 함정, 사람 확인링크가 담긴 정중한 홍보폼마다 돈을 받는 사람콘텐츠 점수, 검토보이는 것보내는 쪽막는 것스팸의 모습에서 그것을 막는 계층까지모든 필드에 무작위 텍스트스크립트 봇HTML을 파싱해 모든 입력을 채움허니팟한 출처에서 분당 수십 건반복 실행되는 스크립트레이트 리밋로드 후 1초 안의 깔끔한 POST헤드리스 브라우저시간 함정, 사람 확인링크가 담긴 정중한 홍보폼마다 돈을 받는 사람콘텐츠 점수, 검토
  1. 모든 필드에 무작위 텍스트가 있다면 모든 입력을 채우는 스크립트입니다. 허니팟이 잡습니다.
  2. 한 출처에서 분당 수십 건의 POST가 온다면 반복 실행되는 스크립트입니다. 레이트 리밋이 막습니다.
  3. 페이지가 로드된 지 1초 안에 보낸 깔끔한 POST는 헤드리스 브라우저에서 옵니다. 시간 함정이나 사람 확인이 막습니다.
  4. 링크가 담긴 정중한 홍보 문구는 폼마다 돈을 받는 사람에게서 옵니다. 콘텐츠 점수와 여러분의 직접 검토만 이를 잡을 수 있습니다.
마지막 줄은 어떤 캡차로도 막을 수 없습니다. 사람이 풀 수 있기 때문에, 콘텐츠 점수와 매주 스팸 폴더를 확인하는 것만이 유일한 방어입니다.

여러 계층을 연결한 문의 폼

스택별로 만든 하나의 문의 폼입니다. 모든 버전에 화면 밖 허니팟과 사람 확인이 들어 있습니다. 브라우저 전용 버전은 키가 필요 없는 Ikiform의 관리형 Turnstile 검사를 사용합니다. 서버 버전은 직접 구현하는 방식을 보여 줍니다. 서버가 여러분의 Cloudflare 키로 Turnstile 토큰을 검증한 다음 필드를 전달합니다. 아래에서 스택을 고르세요. 선택은 이 가이드의 모든 코드 예제에 그대로 적용됩니다.

서버 탭은 작은 헬퍼 두 개를 함께 씁니다. verifyTurnstile은 토큰이 진짜인지 Cloudflare에 묻고, sendToIkiform은 시크릿 키와 방문자의 Origin 헤더를 붙여 필드를 전달합니다. 서버 측 fetch는 자체 Origin을 보내지 않으며, Ikiform은 이 헤더를 읽어 무료 localhost 테스트를 알아봅니다. 두 헬퍼 모두 실패 시 닫힘 방식이라, Cloudflare에 연결할 수 없으면 조용히 통과시키지 않고 방문자에게 오류를 보여 줍니다.

lib/spam.ts
const SITEVERIFY = "https://challenges.cloudflare.com/turnstile/v0/siteverify";
const ENDPOINT = "https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx";

export interface Contact {
  name: string;
  email: string;
  message: string;
  _gotcha: string;
}

export function readContact(data: FormData): Contact {
  return {
    name: String(data.get("name") ?? "").trim(),
    email: String(data.get("email") ?? "").trim(),
    message: String(data.get("message") ?? "").trim(),
    _gotcha: String(data.get("_gotcha") ?? ""),
  };
}

export async function verifyTurnstile(input: { secret: string; token: string; ip?: string | null }) {
  if (!input.token || input.token.length > 2048) return false;
  const body = new URLSearchParams({ secret: input.secret, response: input.token });
  if (input.ip) body.set("remoteip", input.ip);
  try {
    const response = await fetch(SITEVERIFY, { method: "POST", body });
    const result = (await response.json()) as { success?: boolean };
    return result.success === true;
  } catch {
    return false;
  }
}

export async function sendToIkiform(secretKey: string, contact: Contact, origin?: string | null) {
  try {
    const response = await fetch(ENDPOINT, {
      method: "POST",
      headers: {
        Accept: "application/json",
        "Content-Type": "application/json",
        Authorization: `Bearer ${secretKey}`,
        ...(origin ? { Origin: origin } : {}),
      },
      body: JSON.stringify(contact),
    });
    const result = await response.json().catch(() => null);
    if (result?.ok) return { ok: true as const };
    const fields: Record<string, string[]> = result?.error?.fields ?? {};
    const first = Object.values(fields)[0]?.[0];
    return {
      ok: false as const,
      status: response.status,
      message: String(first ?? result?.error?.message ?? "Couldn't send your message. Try again."),
    };
  } catch {
    return { ok: false as const, status: 502, message: "Couldn't reach the form service. Try again." };
  }
}

일반 폼에는 스크립트가 필요 없습니다. Ikiform 폼의 Spam check(스팸 검사)를 Ikiform으로 설정하면, 토큰 없이 들어온 POST에 ikiform.com의 짧은 확인 페이지가 표시되어 Turnstile을 실행하고 필드를 이어서 보냅니다. 보이지 않게 통과하는 사람은 전혀 알아차리지 못합니다.

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 maxlength="120" />

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

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

  <div class="hp" aria-hidden="true">
    <label for="hp-field">Leave this empty</label>
    <input id="hp-field" name="_gotcha" type="text" tabindex="-1" autocomplete="off" />
  </div>

  <button>Send message</button>
</form>

<style>
.hp {
  position: absolute;
  left: -10000px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}
</style>

허니팟은 display: none 대신 화면 밖에 둡니다. 일부 봇은 그런 방식으로 숨겨진 필드를 건너뛰기 때문입니다. 래퍼의 aria-hidden과 입력의 tabindex가 스크린 리더와 키보드가 접근하지 못하게 합니다.

  • 모든 버전은 HTML 탭의 style 블록에 있는 .hp 클래스를 사용합니다. 전역 CSS에 넣어 두세요.
  • 허니팟 이름은 Ikiform 기본값인 _gotcha입니다. 서버 버전은 이 필드를 그대로 전달하므로, 채워진 경우에도 honeypot 사유와 함께 스팸 폴더에 들어갑니다.
  • Turnstile은 폼에 숨겨진 cf-turnstile-response 입력을 추가합니다. 토큰은 300초 동안 유효하고 한 번만 쓸 수 있으므로 시도마다 새 토큰이 필요합니다.
  • Cloudflare의 테스트 키를 쓰면 이 모든 것을 로컬에서 실행할 수 있습니다. Turnstile 가이드에 키 목록이 있습니다.
서버 측 버전은 시크릿 키로 POST하므로 Ikiform은 Allowed domains와 자체 사람 확인을 건너뜁니다. 그러면 모든 POST가 서버의 IP에서 오고, 방문자당 분당 5건인 Standard 제한이 모든 방문자를 합쳐서 셉니다. 서버를 거쳐 POST하는 폼에는 Relaxed 또는 Custom 레이트 리밋을 고르고, IKIFORM_SECRET_KEY는 서버 전용 환경 변수에 두세요.

아래 필드 정의를 직접 시험해 보세요. 브라우저에서 Ikiform의 검증기를 실행하며, 서버가 스팸 점수를 계산하기 전에 적용하는 것과 같은 규칙입니다.

미리보기

Ikiform의 검증기를 브라우저에서 실행합니다. 아무것도 전송되지 않습니다.

문의 폼 템플릿과 같은 필드입니다. 이 정의를 폼의 Fields 탭에 붙여 넣으면 Ikiform은 정의와 맞지 않는 POST를 422 validation_failed로 거부합니다.

허니팟

허니팟은 사람에게는 보이지 않고 봇은 채우는 입력입니다. 값이 들어 있다면 소프트웨어가 보낸 POST입니다. 방문자에게는 아무 부담이 없고, HTML을 파싱해 찾은 입력을 모두 채우는 가장 오래되고 흔한 봇을 잡습니다.

요령 자체보다 잘 숨기는 것이 더 중요합니다. 허니팟을 대충 만들면 세 가지 문제가 생깁니다.

  • 스크린 리더가 읽어 줍니다. 시각 장애가 있는 방문자는 필드가 하나 더 있다고 듣고 채운 뒤 스팸으로 표시됩니다. aria-hidden="true"로 감싸고, 비워 두라는 레이블을 붙이세요.
  • 키보드 포커스가 갑니다. Tab 키로 폼을 이동하다가 보이지 않는 입력에서 멈춥니다. tabindex="-1"로 탭 순서에서 빼세요.
  • 자동 완성이 채웁니다. 브라우저와 비밀번호 관리자는 이름을 보고 필드를 채웁니다. 필드 이름을 email2나 phone으로 지으면 일부는 실제 사람의 정보로 채워 버립니다. _gotcha처럼 어떤 자동 완성도 알아보지 못하는 이름을 쓰고 autocomplete="off"를 추가하세요.

허니팟 가이드에서 숨기는 기법, 봇이 이를 알아채는 방법, 스택별 서버 측 검사를 다룹니다. Ikiform에서는 허니팟이 채워지면 점수에 5가 더해지며, 이것만으로 기준값 5에 도달합니다. 필드 이름은 Honeypot field에서 폼마다 바꿀 수 있습니다.

시간 검사

사람은 문의 폼을 채우는 데 몇 초가 걸립니다. 많은 봇은 몇 밀리초면 끝납니다. 시간 함정은 폼이 렌더링된 시각을 기록해 두고 POST될 때 그 간격을 확인합니다. 단순한 버전은 숨겨진 필드에 타임스탬프를 넣습니다.

시간 함정, 단순한 버전
const started = Number(formData.get("started"));
const seconds = (Date.now() - started) / 1000;
const tooFast = !Number.isFinite(seconds) || seconds < 3;

봇은 원하는 숫자를 아무거나 보낼 수 있으므로, 평범한 타임스탬프는 신경 쓰지 않는 봇만 막습니다. 서버에서 시크릿으로 서명하면 봇은 더 늦은 렌더링 시각을 위조할 수 없습니다. 캡차 없이 스팸 막기 페이지에서 서명된 버전을 만듭니다.

폼이 SDK나 shadcn/ui 컴포넌트에서 온 경우 Ikiform이 렌더링 시각에 대신 서명합니다. 이들은 _ikiform_ts를 보내며, 렌더링 후 2초 안에 보낸 POST는 점수에 3이 더해집니다. 위조되었거나 하루 지난 타임스탬프도 3이 더해집니다. 일반 HTML 폼은 타임스탬프를 보내지 않으며 불이익을 받지 않습니다.

콘텐츠 점수 매기기

어떤 스팸은 사람이 직접 입력하거나 붙여 넣기 때문에 모든 봇 검사를 통과합니다. 정체를 드러내는 것은 내용입니다. 링크, 판매 문구, 일회용 주소, 두 번 보낸 같은 텍스트입니다. 신호 하나만으로 차단하지 말고, 신호마다 점수를 매겨 합계가 높으면 스팸으로 처리하세요. 메시지에 링크 하나는 평범하지만, 이름 필드의 링크는 절대 평범하지 않습니다.

신호Ikiform이 더하는 점수
허니팟이 채워짐+5
이름 또는 제목 필드의 링크+3
최근 24시간 안에 이 폼으로 같은 필드를 보냄+3
서명된 렌더링 시각 후 2초 안에 보냄+3
전체 필드에 걸쳐 링크 3개 이상+2
"seo services" 같은 알려진 스팸 문구 22개 중 하나+2
일회용 이메일 도메인의 주소+2

5점 이상이면 스팸입니다. 약한 신호 하나만으로는 도달하지 않습니다. 링크 3개가 든 실제 메시지는 2점입니다. 두 개가 겹치면 자주 도달합니다. 예를 들어 이름의 링크와 스팸 문구가 함께 있을 때입니다. 콘텐츠 점수를 직접 매긴다면 오래된 호스팅 서비스로 Akismet이 있고, 많은 팀은 위와 같은 정규 표현식 몇 개로 시작합니다.

사람 확인

사람 확인은 브라우저가 사람에 의해 조작되고 있음을 증명하게 합니다. Cloudflare Turnstile 같은 최신 방식은 대부분 보이지 않게 실행되고, 확신이 없을 때만 체크박스를 보여 줍니다. 위젯은 브라우저에 토큰을 주고, 서버는 그 토큰을 제공자에게 보내 검증해야 합니다. 서버에서 검증하지 않는 토큰은 아무것도 막지 못합니다. 봇이 위젯을 건너뛰고 토큰 없이 POST할 수 있기 때문입니다.

Ikiform은 각 폼의 Spam check 아래에 세 가지 설정이 있습니다.

설정동작코드 변경
Off (the default)챌린지 없음. 허니팟, 시간 검사, 점수 매기기는 계속 실행됩니다.없음
IkiformIkiform이 ikiform.com에서 실행하는 Turnstile. Cloudflare 계정이나 키가 필요 없음HTML POST는 없음. fetch POST는 먼저 @ikiform/sdk의 challenge()를 호출
Your own Turnstile keys(자체 Turnstile 키)여러분의 위젯과 시크릿 키. Ikiform이 토큰을 검증하고 호스트 이름을 허용 도메인과 비교폼에 위젯을 렌더링해 cf-turnstile-response를 보내게 함

토큰 없는 fetch POST는 403 challenge_required를 받습니다. Cloudflare에 연결할 수 없으면 Ikiform은 POST를 버리지 않고 captcha_unavailable 사유와 함께 스팸으로 저장하므로 복원할 수 있습니다. 스팸 문서에서 두 흐름을 모두 볼 수 있습니다. Turnstile과 Google의 reCAPTCHA, hCaptcha를 비교하고 있다면 reCAPTCHA 대안을 참고하세요.

레이트 리밋과 허용 도메인

레이트 리밋은 한 방문자가 POST할 수 있는 빈도에 상한을 둡니다. 스팸과 정상 메일을 구분하지는 않고 양을 막습니다. IP와 폼별로 세고, 짧은 버스트는 허용하며, Retry-After 헤더와 함께 429로 응답하세요. 레이트 리밋 가이드에서 스택별로 구현하고, 메모리 내 카운터가 서버리스 호스트에서 실패하는 이유를 설명합니다.

Ikiform은 본문을 읽기 전에 모든 POST를 정확히 셉니다. 폼마다 프리셋을 고릅니다. Standard는 방문자당 분당 5건, Relaxed는 방문자당 분당 20건, Strict는 방문자당 10분에 2건입니다. Standard는 추가로 10초에 최대 3건까지 허용하며, 폼 전체로는 분당 300건을 받습니다. 정직한 사람은 10분 안에 두 번 쓰지 않으므로 문의 폼에는 Strict가 어울립니다. 수치는 레이트 리밋에 있습니다.

Allowed domains(허용 도메인)에는 폼이 있는 사이트를 등록합니다. 다른 페이지에서 온 POST는 스팸으로 표시되고, Reject를 고르면 403 origin_not_allowed로 거부됩니다. 다른 사람이 자기 폼을 여러분의 엔드포인트로 연결하는 것을 막습니다. 작정한 스크립트는 검사하는 헤더를 위조할 수 있으므로 자물쇠가 아니라 하나의 계층으로 여기세요.

스팸은 어떻게 처리되나

봇에게 걸렸다고 알려 주지 마세요. 오류는 무엇을 바꿔야 하는지 가르쳐 주지만, 평범한 성공 페이지는 아무것도 가르쳐 주지 않습니다. 그리고 스팸을 도착하자마자 삭제하지 마세요. 어떤 필터든 가끔 실제 메시지를 잡으며, 삭제한 메시지는 되돌릴 수 없습니다.

스팸으로 판정된 POST는 실제 제출과 같은 성공 응답을 받으므로 봇은 아무것도 알 수 없습니다. spam 상태로 저장되고, 알림 이메일과 웹훅을 보내지 않으며, 사용량에 포함되지 않고, 30일 뒤 삭제됩니다. 스팸을 차단한 워크스페이스에는 매주 월요일 합계, 가장 많이 받은 폼, 주요 사유를 담은 요약 이메일이 한 통 갑니다.

대시보드의 상태 필터에서 Spam을 고르면 각 항목의 점수와 사유를 볼 수 있습니다. Not spam을 누르면 되돌아가고, 그때 이메일과 웹훅이 나갑니다. 월요일 이메일은 워크스페이스 설정의 Weekly spam summary에서 끌 수 있습니다. 스팸은 30일 동안 보관되므로 적어도 한 달에 한 번은 확인하세요.

같은 실제 메시지가 계속 걸린다면 사유를 읽어 보세요. business_name이라는 필드의 링크는 이름 필드의 링크로 점수가 매겨집니다. 필드 이름을 business로 바꾸면 더 이상 이름 필드로 세지 않습니다.

상황별 가이드

아래 페이지들은 방어 수단 하나나 사이트 종류 하나를 더 깊이 다루며, 그 상황에 맞는 코드를 제공합니다.

작동 확인하기

실제로 사용하기 전에 테스트 폼으로 다음을 실행해 보세요. Ikiform에는 무료 플랜이 없습니다. 테스트는 무료입니다. 플랜이 없는 워크스페이스는 localhost에서 보낸 테스트 제출을 매월 50건까지 받습니다. 실제로 운영하려면 제출 500건에 월 $6부터인 Pro를 고르거나, 한 번만 결제하세요. 제출 10,000건에 $49, 50,000건에 $149이며 크레딧은 만료되지 않습니다. 요금을 참고하세요.

  1. 폼의 Allowed domains에 사이트 도메인을 추가하고, 개발하는 동안에는 localhost도 추가하세요. 비워 두면 어디서 오는 POST든 받습니다.
  2. 페이지에서 폼을 채워 제출하세요. 여러분의 감사 페이지나 Ikiform의 호스팅 감사 페이지로 이동합니다.
  3. 대시보드에서 Submissions를 여세요. 보낸 모든 필드가 담긴 새 제출이 있습니다.
  4. 허니팟을 채워 _gotcha=bot으로 POST하세요. 여전히 성공 응답을 받지만, 제출은 스팸으로 저장되고 이메일이나 웹훅을 보내지 않습니다.
  5. 이름 필드에 링크를 넣고 메시지에 seo services 문구를 넣어 보내세요. url_in_name과 keyword 사유와 함께 스팸으로 저장됩니다.
  6. Spam check를 Ikiform으로 바꾸고 일반 HTML 폼을 제출하세요. 잠깐 확인 페이지가 보인 뒤 감사 페이지로 이동합니다.
  7. 폼을 연속으로 몇 번 제출하세요. 제한을 넘으면 폼은 429로 응답하고 방문자에게는 Ikiform의 속도 줄이기 안내 페이지가 표시됩니다.
  8. 아무것도 도착하지 않았나요? 대시보드의 상태 필터를 Spam으로 바꿔 보세요. 스팸은 실제 제출과 같은 성공 응답을 받습니다.

문제 해결

문제해결 방법
제출은 대시보드에 있는데 이메일이나 웹훅이 오지 않음스팸으로 저장되었습니다. 스팸은 아무것도 보내지 않습니다. Spam 필터에서 열어 사유를 확인하고, 스팸 아님으로 표시하면 이메일과 웹훅이 발송됩니다.
403 challenge_required폼의 스팸 검사가 Ikiform으로 설정되어 있는데 fetch POST에 토큰이 없었습니다. 제출할 때마다 먼저 @ikiform/sdk의 challenge(formId)로 토큰을 받거나, 검사를 끄세요.
403 origin_not_allowed, 또는 모든 POST가 스팸으로 들어감페이지의 도메인이 폼의 Allowed domains에 없습니다. 도메인을 추가하고, 로컬 테스트용으로 localhost도 추가하세요. 등록된 도메인의 하위 도메인은 허용됩니다.
429 rate_limited한 방문자가 폼의 레이트 리밋보다 많이 보냈습니다. Standard는 방문자당 분당 5건을 허용합니다. Retry-After만큼 기다리거나, 하나의 공유 IP에서 POST하는 폼에는 Relaxed 또는 Custom을 고르세요.
403 captcha_failedTurnstile 토큰이 없거나, 만료되었거나, 두 번 사용되었습니다. 토큰은 300초 동안 유효하고 한 번만 쓸 수 있으므로, 시도할 때마다 새 위젯을 렌더링하거나 challenge()를 다시 호출하세요.
스팸이 여전히 받음 상태로 들어옴하나를 열어 나머지와 공통점이 무엇인지 읽어 보세요. 스크립트 스팸이라면 Spam check를 Ikiform으로 설정하고, 사람이 입력한 홍보라면 더 엄격한 레이트 리밋과 주간 검토가 필요합니다.
실제 방문자가 보낼 수 없다고 알려 옴허니팟이 스크린 리더에서 숨겨져 있고 자동 완성이 건너뛰는 이름인지, 엄격한 레이트 리밋이 한 사무실 IP를 쓰는 사람들을 잡고 있지 않은지 확인하세요.

질문

막히는 단계가 있거나 내 상황에 맞는 가이드가 필요하다면 이메일을 보내세요: hello@ikiform.com.