How do I create a form with Hodifly?
A contact form is quick to add to a page. Receiving it is another story: you usually need an external service, a subscription, or server-side code. Hodifly forms spare you all three.
You create a form in your HTML, mark it for Hodifly to handle, deploy, and the messages arrive in cPanel → Hodifly → Forms, with an email for every submission. Nothing else to install.
What does this mean for you in practice? Entries are stored in your own account, and there is no limit on submissions other than your disk quota. No plan to keep an eye on, no data held by a third party.
⚠️ Good to know up front: Hodifly forms only work on static sites. A Node or Python application handles its own submissions in its code.
1. How do I create a form?
Add the hodifly attribute to your `` tag, and give it a name:
<form name="contact" data-hodifly method="POST">
<input type="email" name="email" required>
<textarea name="message" required></textarea>
<button>Submit</button>
</form>
Four ways of writing it all work the same: hodifly, data-hodifly, netlify, and data-netlify="true". A site already built for Netlify therefore works with no changes at all: you deploy it as-is.
What happens at deployment
Hodifly detects your forms during the build and automatically:
- injects a hidden
form-namefield — this is what identifies the form on receipt; - injects an invisible bot trap (honeypot): a field that only a bot would fill in;
- generates a small script in the published site;
- routes POST requests to that script.
You don't have to configure anything. The form appears in Forms as soon as it's deployed, even with no entries yet — handy for checking that it has been detected correctly.
What does the visitor see after submitting?
They're sent back to the page they posted from, with ?hf=ok added to the URL. It's up to you to display the thank-you message:
<p id="merci" style="display:none">Thank you, your message has been received.</p>
<script>
if (/[?&]hf=ok/.test(location.search)) document.getElementById('merci').style.display = '';
</script>
To land on a different page, point the form's action at it (action="/merci/"): the visitor will be redirected there after the entry is saved.
If the submission is rejected, the URL receives ?hf=err&hf_why= (see the reasons table further down).
What about AJAX?
If your form is submitted in the background by JavaScript, add the Accept: application/json header to the request: you get {"ok":true} back instead of a redirect, or {"ok":false,"error":"captcha"} if it's rejected.
2. Where can I find received messages?
In cPanel, open Hodifly → Forms. There you'll find:
- the list of all your forms, across every project: name, project, number of entries, date of the last one;
- for each row: View (entries), Settings, Export to CSV;
- on a form's page: ← Forms to go back to the list, then ← Projects to go back to the projects;
- Clear All to empty a form: be careful, this is irreversible.
3. What settings can I change?
Click Settings next to a form. Good news: settings are read on every submission. A change therefore takes effect immediately, with no need to redeploy your site.
Emails
- Email me every entry: enabled by default. Uncheck it to stop receiving anything. Entries are still saved, and the webhook still fires.
- Send entries to: one or more addresses separated by commas. Left blank, Hodifly uses the project's notification address (or, failing that, the cPanel contact).
The email uses the same formatting as deployment notifications. Its Reply-To field points to the visitor's address as soon as the form contains an email field: you can reply straight from your inbox, no copy-pasting needed.
Captcha
Choose Cloudflare Turnstile, hCaptcha, or reCAPTCHA v2, then paste the provider's secret key.
The division of labor is simple: you add the provider's widget to your form, Hodifly verifies the token server-side. If verification fails, the entry is rejected (hf_why=captcha). When in doubt, Hodifly rejects: a configured captcha is always enforced.
File uploads
Enable Allow file uploads, then set a maximum size (in MB) and a list of allowed extensions (pdf, png, jpg).
Your form must include enctype="multipart/form-data": this is the HTML attribute that lets a form carry files:
<form name="contact" data-hodifly method="POST" enctype="multipart/form-data">
<input type="file" name="piece-jointe" accept=".pdf,.png,.jpg">
...
</form>
Files are stored outside the web root - so they're never publicly accessible - and can be downloaded from the form's page. A disallowed extension returns hf_why=filetype, an oversized file returns hf_why=filesize.
The size is still capped by what PHP accepts on the server: the actual limit is shown below the setting.
Outgoing webhook
A webhook is an automatic notification sent to another application whenever an event occurs — here, for every form entry. Useful for feeding a CRM, a team channel, or an internal tool.
Enter a URL (https://…) and, if you wish, a signing key. Every entry is sent there as JSON.
4. What is the webhook format?
Hodifly sends a POST request with Content-Type: application/json and a payload close to Netlify's:
{
"payload": {
"id": "93ee97f9f1769bea",
"form_name": "contact",
"site_url": "https://exemple.re/",
"created_at": "2026-07-15T04:11:11+00:00",
"data": {
"email": "jeanne@exemple.com",
"message": "Bonjour",
"ip": "203.0.113.10",
"user_agent": "Mozilla/5.0 …"
},
"human_fields": {
"Email": "jeanne@exemple.com",
"Message": "Bonjour"
},
"ordered_human_fields": [
{ "title": "Email", "name": "email", "value": "jeanne@exemple.com" },
{ "title": "Message", "name": "message", "value": "Bonjour" }
],
"files": [
{ "field": "piece-jointe", "name": "cv.pdf", "size": 20481, "stored": "20260715-041111-a1b2c3d4-cv.pdf" }
]
}
}Champ | Contenu |
|---|---|
| Unique identifier of the entry |
| Your form's |
| The URL of the site that received the submission |
| UTC date/time (ISO 8601) |
| The raw fields, plus |
| The same fields with a readable label |
| Same, keeping the form's field order |
| The submitted files (the content stays in your account) |
Verifying the signature
If you've set a key, the request carries the following header:
X-Hodifly-Signature: sha256=<HMAC-SHA256 of the raw body, using your key>This signature lets you verify that the request really comes from your site, not from a third party. Verify it against the raw body of the request, before any JSON decoding. Example in PHP:
$raw = file_get_contents('php://input');
$expected = 'sha256=' . hash_hmac('sha256', $raw, 'YOUR_KEY');
if (!hash_equals($expected, $_SERVER['HTTP_X_HODIFLY_SIGNATURE'] ?? '')) {
http_response_code(403); exit('invalid signature');
}
$entry = json_decode($raw, true)['payload'];5. Why was a submission rejected? (hf_why)
Valeur | Signification |
|---|---|
| The captcha wasn't validated |
| Disallowed file extension |
| File exceeds the form's maximum size |
| Submission exceeds what PHP accepts on the server |
| File upload failed |
6. Is my data protected?
- Anti-bot. An invisible honeypot field is injected automatically. A bot that fills it in gets a normal response, but nothing is saved or sent — it never knows it was filtered out.
- CSRF. A submission coming from another site is rejected: Hodifly checks the origin of the request, which prevents a third party from posting on your behalf.
- No open relay. The handler only accepts forms that were actually detected during the last deployment.
- Storage. Entries and files are stored outside the web root, with
0600permissions, and survive both deployments and restores. - Privacy. Everything stays on your account. Entries never pass through Hodifly.
7. What are the limitations?
- Static sites only. A Node or Python application handles its own submissions in its code.
- A form removed from your HTML no longer accepts submissions, but keeps the entries already received.
- File contents aren't attached to emails: they're downloaded from cPanel.
- No Akismet-style spam filtering: the honeypot and captcha cover most needs.
8. If something goes wrong
Symptôme | À vérifier |
|---|---|
The form doesn't appear in Forms | Is the |
"Unknown form" | The form didn't exist at the last deployment: redeploy. |
The file isn't saved | Are file uploads enabled in Settings? Is the extension allowed? Does the form have |
No email | Is the Email me every entry box checked? Is the address valid? |
The webhook isn't receiving anything | The URL must be |
Updated on: 17/07/2026
Thank you!