Both services are built on the same idea: a plain HTML form posts to an endpoint, no backend required. That makes the migration mostly mechanical - but only mostly, because the two products name their control fields differently, and two of Web3Forms' fields behave in ways that are easy to carry over incorrectly. This guide maps every documented Web3Forms customization to its WebForms equivalent, with the differences called out rather than glossed over.
The minimal change: one attribute
A Web3Forms form identifies itself with a hidden access_key input posted to a shared endpoint. WebForms puts the key in the endpoint URL itself, so the hidden input can simply be deleted:
<form action="https://api.web3forms.com/submit" method="POST"> <input type="hidden" name="access_key" value="YOUR_ACCESS_KEY"> <input type="text" name="name" required> <input type="email" name="email" required> <textarea name="message" required></textarea> <button type="submit">Send</button> </form>
<form action="https://send.webforms.to/wft_your_key" method="POST"> <input type="text" name="name" required> <input type="email" name="email" required> <textarea name="message" required></textarea> <button type="submit">Send</button> </form>
If your deployment process makes it easier to keep the key in the form body - some site builders template the action URL separately from the fields - that works too: post to https://send.webforms.to/ and keep a hidden access_key input holding your wft_ key. Both spellings hit the same handler.
Your data fields need no changes at all. Neither service imposes a naming scheme on name, email, message or anything else you collect; every non-control field is captured as submitted.
The control-field mapping
Web3Forms uses bare names for its control fields (subject, redirect). WebForms prefixes directives with an underscore (_subject, _redirect) so they can never collide with a real data field - a form that literally asks its visitor for a subject line stays unambiguous. The renames:
- access_key -> moves into the endpoint URL (or stays as access_key in the body - both work)
- subject -> _subject: sets the notification email subject line
- redirect -> _redirect: full URL to send the visitor to after a successful submit
- replyto -> _replyto: overrides the Reply-To on the notification email
- from_name -> _from_name: overrides the sender display name on the notification
- botcheck -> delete it, or rename to _gotcha (see the spam section below)
- cc -> _cc: comma-separated additional recipients, validated per address
One behavior carries over without any field at all: like Web3Forms, if your form has an email field, replies to the notification go to the submitter automatically. You only need _replyto to override that default.
Redirects: same idea, same constraint
Web3Forms requires the redirect value to be an absolute https:// URL, and so do we - a relative path is rejected by validation rather than guessed at. One difference in your favor: cross-domain redirects are a paid feature on Web3Forms, while _redirect accepts any https URL on every WebForms plan, including free. If you were paying partly for that, the line item disappears.
<input type="hidden" name="_redirect" value="https://yoursite.com/thanks.html">
Forms that submit via fetch and handle the response in JavaScript should keep doing their own client-side navigation, exactly as Web3Forms recommends for its own redirect field.
Spam protection: what replaces botcheck
Web3Forms' honeypot is a checkbox named botcheck, and their docs now mark it deprecated with a recommendation to use a captcha instead. When you migrate, delete the botcheck input and decide what you actually want:
- The built-in honeypot: add an input named _gotcha, hide it with CSS, and any submission that fills it is rejected outright. Configurable per form, including a custom field name so trained bots that skip _gotcha get caught anyway.
- The time trap: enabled by default, no markup needed. A form submitted faster than a human can type is rejected.
- A captcha: Turnstile, hCaptcha and reCAPTCHA are all supported; the token field is picked up automatically from the standard widget names.
- The layered filter: disposable-email blocking, keyword and domain blocklists, and heuristics for link floods and markup injection run on every submission regardless, with a per-form threshold controlling when something is quarantined for review instead of delivered.
The practical difference from a relay service: a quarantined submission is not gone. It sits in the dashboard where you can read it, mark it as not-spam, and the filter learns from the correction. With a relay, a false positive is mail you never knew existed.
AJAX forms
If you submit with fetch and read the JSON response, the shape is familiar: POST the same fields to your endpoint with an Accept: application/json header and you get { ok: true, ... } back, or { ok: false, error, message } with a stable machine-readable error code on failure. Swap the URL, drop the access_key from the payload, and your existing success/error handling logic keeps working.
What exists after the switch that didn't before
The migration above gets you feature parity. The reason to do it is what sits behind the same POST once it lands: every submission stored and searchable instead of existing only as an email, one form fanning out to Slack, Discord, Telegram and webhooks alongside the inbox, and a per-destination delivery log that answers "did that notification actually send?" with a status and a response body instead of a guess. None of that requires further changes to your form - destinations are configured in the dashboard, so adding Slack next month touches zero markup.
The 10-minute checklist
- Create a form in the dashboard and copy its endpoint URL.
- Swap the action attribute and delete the hidden access_key input.
- Rename control fields: subject to _subject, redirect to _redirect, replyto to _replyto, from_name to _from_name.
- Delete the botcheck input; add a hidden _gotcha field if you want the honeypot.
- Add your email destination in the dashboard, plus any others.
- Submit the form once for real and watch it arrive in the dashboard and the delivery log.
Field names verified against the Web3Forms documentation on September 3, 2026. If their field names change after that date, their docs are the source of truth for the left-hand side of every mapping above.