Guides

Guides17 min de lecture

Comment fonctionne l'envoi d'un formulaire HTML

Ce que fait un navigateur quand un formulaire est envoyé : quels champs il transmet, comment il les encode, ce que le serveur doit répondre, et comment Ikiform répond.

Preet SutharFondateur, Ikiform

Quand quelqu'un appuie sur le bouton d'envoi, le navigateur vérifie les contraintes du formulaire, rassemble chaque contrôle nommé et activé, les encode selon l'enctype du formulaire et les envoie à l'URL action avec la method du formulaire. Le serveur répond comme pour n'importe quel chargement de page, et un serveur bien conçu répond à un POST réussi par une redirection 303, pour qu'un rafraîchissement n'envoie pas le formulaire deux fois. Faites pointer action vers un backend de formulaires comme Ikiform et vous obtenez cette redirection, la validation des champs et le filtrage du spam sans serveur à vous.

Les formulaires HTML sont plus anciens que JavaScript et restent le moyen le plus fiable d'envoyer des données depuis une page web. Ils fonctionnent avec les scripts désactivés, sur des téléphones lents et dans tous les créateurs de sites qui vous laissent coller du balisage. La plupart des bugs de formulaire viennent d'une méconnaissance de ce que fait le navigateur de lui-même, donc ce guide décrit un envoi étape par étape, puis montre où s'insère un backend de formulaires.

Ce qui se passe quand vous appuyez sur envoyer

Un envoi est une navigation normale accompagnée d'un corps. Le navigateur fait quatre choses avant que quoi que ce soit ne quitte la page, puis confie la requête au réseau et charge ce qui revient.

Ce que fait un navigateur entre le clic sur envoyer et la page suivantedans le navigateursur le réseauenvoibouton, toucheEntrée ourequestSubmit()vérificationdescontraintesattributsrequired,type, pattern,min, maxjeu dedonnées duformulairecontrôlesnommés etactivésuniquementencodageurlencoded oumultipartmessagesur lechamprien n'estenvoyéPOST versactionSec-Fetch-Mode:navigatele serveurrépond303 SeeOtherGETLocationla page deremerciementvalideinvalidesuivreCe que fait un navigateur entre le clic sur envoyer et la page suivantedans le navigateursur le réseauenvoibouton, toucheEntrée ourequestSubmit()vérificationdes contraintesattributs required,type, pattern, min,maxjeu de donnéesdu formulairecontrôles nommés etactivés uniquementencodageurlencoded oumultipartmessagesur lechamprien n'estenvoyéPOST versactionSec-Fetch-Mode:navigatele serveurrépond303 See OtherGET Locationla page deremerciementvalidesuivreinvalide
  1. Un bouton d'envoi, la touche Entrée dans un champ texte ou form.requestSubmit() démarre l'envoi et déclenche un événement submit.
  2. Sauf si le formulaire a novalidate, le navigateur vérifie les contraintes de chaque contrôle. Le premier contrôle invalide reçoit un message et rien n'est envoyé.
  3. Le navigateur construit le jeu de données du formulaire à partir de chaque contrôle qui a un nom et n'est pas désactivé.
  4. Il encode ce jeu selon l'enctype du formulaire, en urlencoded sauf indication contraire.
  5. Il navigue vers l'URL action avec la méthode du formulaire, en envoyant le corps.
  6. Le serveur répond. Un 303 indique au navigateur de charger une autre URL en GET, qui est la page sur laquelle le visiteur arrive.
Tout ce qui est au-dessus de la ligne se passe sans aucun réseau. Un écouteur de l'événement submit peut arrêter le processus à la première étape et prendre le relais avec fetch.

Les attributs qui déterminent la requête

Cinq attributs de <form> déterminent où vont les données et comment. Chacun peut être remplacé pour un seul bouton avec l'attribut form* correspondant : c'est ainsi qu'un formulaire peut avoir des boutons Send et Save draft au comportement différent.

AttributPar défautCe qu'il faitRemplacement par bouton
actionL'URL de la page elle-mêmeL'URL qui reçoit les données. Les valeurs relatives sont résolues par rapport à la page. Voir l'attribut actionformaction
methodgetget place les champs dans les paramètres d'URL. post les envoie comme corps. dialog ferme un <dialog> sans rien envoyerformmethod
enctypeapplication/x-www-form-urlencodedLa façon dont un corps post est encodé. multipart/form-data est nécessaire pour les fichiers. text/plain est destiné au débogageformenctype
novalidateDésactivéeIgnore la vérification des contraintes du navigateur à l'envoiformnovalidate
target_selfL'endroit où la réponse se charge. _blank ouvre un nouvel onglet, une iframe nommée garde la page en placeformtarget

Deux boutons, deux comportements. Le second ignore la validation, et le name et la value du bouton cliqué sont ajoutés aux données, pour que le serveur sache lequel a été pressé :

Deux boutons d'envoi
<form action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post">
  <input name="email" type="email" required />
  <textarea name="details" required></textarea>
  <button name="intent" value="report">Send report</button>
  <button name="intent" value="draft" formnovalidate>Save as draft</button>
</form>

Quels contrôles sont envoyés

Les navigateurs n'envoient que ce que la spécification HTML appelle le jeu de données du formulaire. La plupart des surprises dans un journal de soumissions viennent de ces règles :

  • Un contrôle a besoin d'un name. Un id seul n'envoie rien.
  • Les contrôles disabled sont ignorés. Utilisez readonly si vous voulez une valeur affichée, verrouillée et envoyée.
  • Une case à cocher ou un bouton radio non coché n'envoie rien du tout, pas false. Une case cochée sans value envoie on.
  • Seul le bouton qui a envoyé le formulaire est inclus, avec son name et sa value.
  • <select multiple> et les groupes de cases à cocher qui partagent un nom envoient une paire par valeur sélectionnée.
  • Les inputs en dehors de l'élément <form> peuvent le rejoindre avec un attribut form qui désigne l'id du formulaire.

Comment le corps est encodé

Avec l'enctype par défaut, le corps est une seule ligne de paires name=value jointes par &, avec les espaces en + et tout ce qui sort d'un ensemble sûr encodé en pourcentage. Voici la vraie requête du formulaire de signalement plus bas, telle que le navigateur l'envoie :

application/x-www-form-urlencoded
POST /f/frm_xxxxxxxxxxxxxxxx HTTP/1.1
Host: ikiform.com
Content-Type: application/x-www-form-urlencoded
Origin: https://example.com
Sec-Fetch-Mode: navigate

email=ada%40example.com&page=https%3A%2F%2Fexample.com%2Fpricing&severity=broken&details=The+annual+toggle+does+nothing&_redirect=https%3A%2F%2Fexample.com%2Freport-sent&_gotcha=

multipart/form-data enveloppe chaque champ dans sa propre partie avec une chaîne de délimitation choisie par le navigateur. C'est plus volumineux, mais cela peut transporter des fichiers. Ikiform lit les deux. text/plain n'a aucun moyen fiable d'indiquer où se termine une valeur, donc Ikiform le refuse avec 415 unsupported_media_type.

multipart/form-data
POST /f/frm_xxxxxxxxxxxxxxxx HTTP/1.1
Host: ikiform.com
Content-Type: multipart/form-data; boundary=----formdata7f3a

------formdata7f3a
Content-Disposition: form-data; name="email"

ada@example.com
------formdata7f3a
Content-Disposition: form-data; name="severity"

broken
------formdata7f3a--
Quel que soit l'encodage, gardez la page en UTF-8. Ikiform décode chaque corps en UTF-8 et refuse les autres jeux de caractères avec 415 unsupported_charset. L'envoi de fichiers n'est pas encore pris en charge. Un envoi avec un fichier reçoit 422 files_not_supported.

Quatre façons d'envoyer un formulaire

Un envoi classique n'est qu'une option. Chaque approche ci-dessous est un choix valable dans certains cas ; ce qui change, c'est qui traite la réponse et ce qui se passe avec les scripts désactivés.

ApprochePoints fortsCompromis
method="post" classique avec une redirectionFonctionne sans JavaScript, dans n'importe quel créateur de sites. Le navigateur gère l'état de chargement, le retour et le rafraîchissementUn chargement de page complet. Les erreurs de champ nécessitent une page rendue côté serveur, ou la page d'erreur du backend
fetch avec FormDataPas de rechargement. Vous décidez de l'apparence du succès et de chaque erreur de champL'état de chargement, les erreurs réseau et le focus sont à votre charge. Sans précaution, cela casse avec les scripts désactivés. Voir sans rechargement de page
Les actions de formulaire des frameworks, comme les server actions Next.js ou les form actions SvelteKitVotre serveur voit l'envoi en premier, et le même formulaire fonctionne avant et après l'hydratationIl vous faut un runtime serveur, et la requête vers le backend part de votre serveur, ce qui change les limites de débit et les vérifications d'origine
method="get"Formulaires de recherche et de filtre. Le résultat a une URL partageableLes champs se retrouvent dans l'URL, l'historique et les journaux du serveur. Jamais pour des données personnelles ni pour une action qui modifie un état

Commencez par l'envoi classique et ajoutez fetch par-dessus. Ainsi, le formulaire fonctionne encore quand un script ne se charge pas, ce qui arrive plus souvent que ne le montrent la plupart des outils d'analyse.

Ce que le serveur doit répondre

Le navigateur traite la réponse à un POST comme une nouvelle page. Si cette page est la réponse elle-même, avec le statut 200, le navigateur retient qu'elle vient d'un POST. Rafraîchissez et il propose de renvoyer le formulaire ; faites retour puis avancer et certains navigateurs font de même. C'est ainsi que des gens paient deux fois et que le support reçoit trois fois le même message.

La solution est le pattern Post/Redirect/Get. Le serveur traite le POST, puis répond 303 See Other avec un en-tête Location. Le navigateur charge cette URL en GET, et la page que voit le visiteur vient d'un GET, donc un rafraîchissement est sans conséquence.

Répondre à un POST avec une page comparé à une réponse par une redirection 303répondre avec une page (200)répondre avec une redirection (303)POST/reportHTML deremerciementen 200la page vientd'un POSTrafraîchir« Confirmer lenouvel envoi duformulaire »nouveauPOSTun doublonPOST/report303 SeeOtherLocation:/report-sentGET/report-sentrafraîchirne recharge quele GETRépondre à un POST avec une page comparé à une réponse par une redirection 303répondre avec une page (200)répondre avec une redirection (303)POST /reportHTML de remerciement en200la page vient d'un POSTrafraîchir« Confirmer le nouvel envoi duformulaire »nouveau POSTun doublonPOST /report303 See OtherLocation: /report-sentGET /report-sentrafraîchirne recharge que le GET
  1. Avec une réponse 200, le navigateur envoie le formulaire et affiche le corps de la réponse comme page.
  2. Cette page est liée au POST, donc un rafraîchissement demande de renvoyer le formulaire et une confirmation renvoie les données.
  3. Avec une réponse 303, le navigateur envoie le formulaire et reçoit l'instruction de charger une autre URL.
  4. Il charge cette URL en GET. Un rafraîchissement ne répète que le GET, donc rien n'est envoyé deux fois.
Ikiform répond toujours à un envoi de navigateur par un 303, donc votre formulaire obtient ce comportement sans effort.
StatutCe que fait ensuite le navigateurÀ utiliser pour
303 See OtherGET de la Location, corps abandonnéAprès tout envoi de formulaire réussi
302 FoundLes navigateurs passent en GET en pratique, mais la spécification permet de conserver la méthodeLes serveurs plus anciens. Préférez 303
307 et 308Répète le même POST avec le même corps à la nouvelle URLDéplacer un endpoint, jamais une page de remerciement
200 avec du HTMLAffiche la page ; un rafraîchissement propose de renvoyerRéafficher le formulaire avec les erreurs
4xx avec du HTMLAffiche la page d'erreurLes erreurs que le visiteur peut corriger en revenant en arrière
Post/Redirect/Get
HTTP/1.1 303 See Other
Location: https://example.com/report-sent
X-Request-Id: req_01k7v2c9t3e8m5q4x6z0a1b2c3

GET /report-sent HTTP/1.1
Host: example.com

Comment Ikiform répond

Un seul endpoint, https://ikiform.com/f/<formId>, répond à la fois aux navigateurs et au code. Il décide à chaque requête s'il répond par des pages ou en JSON, d'après les en-têtes que le navigateur définit de lui-même :

RequêteRéponse
Accept inclut application/jsonJSON
Le corps est en JSONJSON
Sec-Fetch-Mode: navigate, ce qui est le cas de tout envoi de formulaire classiqueRedirection ou page hébergée
Tout autre Sec-Fetch-Mode, comme cors depuis fetchJSON
Pas de Sec-Fetch-Mode, et Accept inclut text/htmlRedirection ou page hébergée
Tout le reste, comme curlJSON

Sec-Fetch-Mode est un en-tête que le navigateur définit et que les scripts de la page ne peuvent pas modifier. Un envoi de formulaire qui fait naviguer l'onglet envoie navigate ; un appel fetch envoie cors pour une requête cross-site. Le même formulaire reçoit donc une redirection quand il est envoyé de façon classique et du JSON quand votre script l'envoie, sans aucun réglage de votre côté. Le tableau complet se trouve dans JSON ou redirection.

Le même endpoint Ikiform qui répond à un envoi de formulaire classique et à un appel fetchenvoi de formulaire simplefetch, ou Accept: application/jsonle navigateurnavigueSec-Fetch-Mode:navigateIkiformmode page303 vers votrepageou la page deremerciement hébergéepage d'erreurhébergéeproblèmes par libelléde champ, vrai statutle script envoieSec-Fetch-Mode: corsIkiformmode JSON201 avecl'identifiant{ ok: true, id }erreur JSONerror.fields paridentifiant de champrefusérefuséLe même endpoint Ikiform qui répond à un envoi de formulaire classique et à un appel fetchenvoi de formulaire simplefetch, ou Accept: application/jsonle navigateurnavigueSec-Fetch-Mode:navigateIkiformmode page303 vers votrepageou la page deremerciementhébergéepaged'erreurhébergéeproblèmespar libelléde champ,vrai statutle script envoieSec-Fetch-Mode:corsIkiformmode JSON201 avecl'identifiant{ ok: true, id }erreurJSONerror.fieldsparidentifiantde champrefusérefusé
  1. Un envoi de formulaire classique fait naviguer l'onglet, donc le navigateur envoie Sec-Fetch-Mode: navigate.
  2. Ikiform répond en mode page : une redirection 303 en cas de succès, ou une page d'erreur hébergée avec le vrai statut de l'erreur.
  3. Un appel fetch envoie Sec-Fetch-Mode: cors, ou votre code demande du JSON avec l'en-tête Accept.
  4. Ikiform répond en mode JSON : 201 avec l'identifiant de soumission, ou l'enveloppe d'erreur avec les erreurs de champ indexées par identifiant de champ.
Vous ne configurez pas le mode. Il découle de la façon dont la requête a été faite.

Où aboutit un envoi réussi

Un envoi de navigateur réussi reçoit un 303. Ikiform choisit la cible dans cet ordre :

  1. Le paramètre Redirect after submit du formulaire dans le tableau de bord. Toute URL http: ou https: fonctionne, car seul votre espace de travail peut la définir.
  2. Un champ _redirect dans l'envoi, ou _next s'il n'y a pas de _redirect, quand c'est une URL https: sans nom d'utilisateur, mot de passe ni port personnalisé, sur l'un des Allowed domains du formulaire ou l'un de leurs sous-domaines.
  3. Sinon, la page de remerciement hébergée à /f/<formId>/thanks. Quand l'envoi vient d'une page https: sur un domaine autorisé, elle renvoie un lien vers cette page.

Un _redirect qui enfreint une règle est ignoré, jamais traité comme une erreur, et un formulaire sans domaines autorisés ignore entièrement _redirect. Cela empêche quiconque d'utiliser votre formulaire pour renvoyer les visiteurs vers le site de son choix. Redirection après envoi traite en détail des pages de remerciement, et les règles complètes se trouvent dans Formulaires HTML.

Quand un envoi est refusé

En mode page, un envoi refusé affiche une page d'erreur hébergée avec le vrai statut HTTP de l'erreur. Pour une erreur de validation, elle liste chaque problème sous le libellé de votre champ et renvoie un lien vers la page d'où vient l'envoi. Le bouton Back du navigateur ramène généralement au formulaire avec ce que le visiteur a saisi ; le lien recharge la page à neuf. En mode JSON, la même erreur revient sous forme d'une seule enveloppe. Chaque code figure dans Erreurs.

422 en mode JSON
{
  "ok": false,
  "error": {
    "code": "validation_failed",
    "message": "Some fields are invalid.",
    "fields": {
      "page": ["Enter a valid URL"],
      "details": ["Must be at least 20 characters"]
    }
  },
  "request_id": "req_01k7v2c9t3e8m5q4x6z0a1b2c3"
}

Le spam est la seule chose qui ne ressemble jamais à une erreur. Un envoi évalué comme spam reçoit le même 303 ou 201 qu'un vrai, donc un bot n'apprend rien, et il n'envoie ni e-mail ni webhook.

Le formulaire dans votre stack

Voici un vrai formulaire, un signalement de page cassée, dans chaque stack. Choisissez la vôtre ; le choix s'applique à chaque exemple de code de ces guides. La définition de champs ci-dessous est celle qu'Ikiform impose sur le serveur, alors essayez : laissez les détails trop courts ou saisissez example comme adresse et vous obtenez le 422 que recevrait votre formulaire.

Aperçu

Exécute le validateur d'Ikiform dans votre navigateur. Rien n'est envoyé.

Collez cette définition dans l'onglet Fields du formulaire dans le tableau de bord et Ikiform valide chaque envoi par rapport à elle. Voir Champs et validation.

Aucun script. Le navigateur vérifie required, type et minlength, envoie les champs en texte urlencoded, suit le 303 d'Ikiform vers votre page _redirect, et affiche la page d'erreur d'Ikiform si le serveur refuse l'envoi.

report.html
<form action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post">
  <label for="email">Your email</label>
  <input id="email" name="email" type="email" autocomplete="email" required />

  <label for="page">Page address</label>
  <input id="page" name="page" type="url" required placeholder="https://example.com/pricing" />

  <label for="severity">How bad is it</label>
  <select id="severity" name="severity">
    <option value="cosmetic">Looks wrong</option>
    <option value="broken">Something doesn't work</option>
    <option value="blocking">I can't continue</option>
  </select>

  <label for="details">What happened</label>
  <textarea id="details" name="details" rows="5" minlength="20" maxlength="4000" required></textarea>

  <input type="hidden" name="_redirect" value="https://example.com/report-sent" />
  <input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />
  <button>Send report</button>
</form>
Les versions côté serveur envoient avec une clé secrète, donc Ikiform ignore Allowed domains et sa vérification humaine. Chaque envoi vient alors de l'IP de votre serveur, et la limite Standard de 5 par minute et par visiteur compte tous vos visiteurs ensemble. Choisissez la limite de débit Relaxed ou Custom pour un formulaire que vous envoyez via un serveur, et gardez IKIFORM_SECRET_KEY dans des variables d'environnement réservées au serveur.

Chaque version serveur transmet l'en-tête Origin du navigateur quand il existe, car un fetch côté serveur n'en envoie pas et Ikiform s'en sert pour reconnaître les tests gratuits depuis localhost sur un espace de travail sans offre.

  • Chaque version garde une vraie method et, quand elle envoie directement à Ikiform, une vraie action. Avec les scripts désactivés, le formulaire s'envoie encore et reçoit le 303.
  • Le name de chaque input est l'id du champ dans la définition. Une différence, comme url au lieu de page, reçoit 422 avec Unknown field "url".
  • L'input masqué _gotcha est le honeypot. Les bots qui le remplissent sont stockés comme spam, discrètement.
  • Les versions JavaScript affichent error.fields sous chaque input, en utilisant le premier message de chaque champ. La version HTML simple laisse cela à la page d'erreur d'Ikiform.

La documentation va plus loin pour chaque framework : Next.js, Astro, SvelteKit, Nuxt, React, Vue et depuis un serveur.

Deux niveaux de validation

La validation du navigateur et celle du serveur n'ont pas le même rôle. La vérification des contraintes du navigateur (required, type="email", minlength, pattern) donne un retour immédiat, mais n'importe qui peut la contourner : un bot envoie directement, et un script peut appeler form.submit(), qui n'exécute jamais la vérification. Le serveur est le seul endroit où une règle tient vraiment.

RègleDans le navigateurDans la définition de champs d'Ikiform
Obligatoirerequired"required": true
E-mailtype="email""type": "email", avec allowed_domains en option
URLtype="url""type": "url", avec https_only en option
Longueurminlength, maxlengthvalidation.min_length, max_length
Seulement les choix listésUn <select>, qu'un bot ignoreoptions ; tout le reste est refusé
Aucun champ supplémentaireImpossibleTout champ absent de la définition est refusé

Sans définition, Ikiform accepte n'importe quels champs et les stocke tels qu'ils arrivent. Publiez-en une et il valide, stocke les nombres et les cases à cocher comme de vrais nombres et booléens, et refuse l'envoi avec 422 validation_failed avant que quoi que ce soit ne soit stocké. Validation montre comment faire dire la même chose aux deux niveaux.

Envoyer avec JavaScript

Pour garder le visiteur sur la page, écoutez submit, appelez preventDefault() et envoyez new FormData(form) avec fetch. Quelques détails décident si cela fonctionne du premier coup :

  • Ne définissez pas Content-Type vous-même quand le corps est un FormData. Le navigateur définit multipart/form-data avec la délimitation ; un en-tête écrit à la main n'a pas de délimitation et le corps ne peut pas être analysé.
  • Envoyez Accept: application/json. Ikiform choisirait de toute façon JSON d'après Sec-Fetch-Mode, mais l'en-tête le rend explicite, et c'est l'un des en-têtes de requête que les navigateurs peuvent envoyer en cross-site sans preflight.
  • Un corps FormData ou URLSearchParams est une requête cross-site simple, donc il n'y a pas de preflight. Un corps JSON avec Content-Type: application/json déclenche d'abord un preflight ; Ikiform y répond, mais cela coûte un aller-retour.
  • form.submit() ignore la validation et l'événement submit. form.requestSubmit() exécute les deux, comme le ferait un clic.
  • Passez le bouton cliqué en second argument, new FormData(form, event.submitter), si son name et sa value comptent.

Le pattern complet, avec état de chargement, focus et erreurs réseau, se trouve dans sans rechargement de page. La documentation JavaScript et fetch liste chaque en-tête et format de corps que l'endpoint accepte.

Choisissez votre cas

Chacune de ces pages reprend une partie de l'envoi et va plus loin que la vue d'ensemble ci-dessus, avec du code pour ce cas.

Vérifiez que tout fonctionne

Faites ces vérifications une fois par formulaire, dans une fenêtre de navigation privée. Ikiform n'a pas d'offre gratuite. Les tests sont gratuits : un espace de travail sans offre accepte 50 soumissions de test par mois depuis localhost. Pour passer en production, choisissez Pro à partir de 6 $/mois pour 500 soumissions, ou payez une seule fois : 10 000 soumissions pour 49 $ ou 50 000 soumissions pour 149 $, avec des crédits qui n'expirent jamais. Voir les tarifs.

  1. Dans Allowed domains du formulaire, ajoutez le domaine de votre site, plus localhost pendant le développement. Laissez vide pour accepter les envois de partout.
  2. Désactivez JavaScript dans les outils de développement de votre navigateur et envoyez le formulaire. Il doit quand même arriver sur votre page de remerciement avec un 303.
  3. Avec JavaScript activé, ouvrez le panneau Réseau et envoyez à nouveau. La requête vers /f/<formId> doit répondre 201 avec du JSON, et la page ne doit pas se recharger.
  4. Envoyez avec un message trop court. L'envoi classique affiche la page d'erreur d'Ikiform avec le libellé de votre champ ; l'envoi par script affiche le message sous le champ.
  5. Sur la page de remerciement, rafraîchissez. Rien ne doit être renvoyé, et aucune seconde soumission ne doit apparaître.
  6. Ouvrez Submissions dans le tableau de bord. La nouvelle soumission y est, avec tous les champs envoyés.
  7. Envoyez avec le honeypot rempli, _gotcha=bot. L'envoi reçoit quand même une réponse de succès, mais la soumission est stockée comme spam et n'envoie ni e-mail ni webhook.

Dépannage

ProblèmeQue faire
405 Method Not Allowed après l'envoiLe formulaire a envoyé vers votre propre hébergeur statique, qui n'accepte pas de POST. Vérifiez action : ce doit être l'URL complète https://ikiform.com/f/<formId>, pas un chemin relatif ni une valeur vide.
Ouvrir l'endpoint dans un onglet affiche 404 not_foundC'est normal. Un onglet envoie un GET, et l'endpoint n'accepte que POST. Vérifiez que la method du formulaire est post ; un formulaire sans méthode envoie un GET avec les champs dans l'URL.
415 unsupported_media_typeLe formulaire a enctype="text/plain", ou un script a envoyé un type qu'Ikiform ne lit pas. Retirez l'enctype, ou utilisez multipart/form-data.
422 validation_failedL'envoi ne correspond pas à la définition des champs du formulaire. error.fields nomme chaque champ et la raison. Faites correspondre le name de l'input à l'id du champ.
Le navigateur arrive sur la page de remerciement d'Ikiform au lieu de la vôtre_redirect est ignoré sauf si son domaine figure dans Allowed domains et qu'il s'agit d'une URL https:. Ajoutez le domaine, ou définissez plutôt Redirect after submit dans les réglages du formulaire.
Un champ manque dans la soumissionIl n'a pas de name, il est disabled, ou c'est une case à cocher non cochée. Donnez-lui un name, utilisez readonly au lieu de disabled, ou définissez un champ checkbox pour qu'une case non cochée soit stockée comme false.
Le rafraîchissement demande de renvoyer le formulaireLa page a répondu au POST avec 200. Les envois directs vers Ikiform reçoivent toujours un 303 ; si votre propre serveur se trouve entre les deux, redirigez après avoir traité l'envoi.
403 origin_not_allowed, ou tous les envois finissent en spamLe domaine de la page n'est pas dans les Allowed domains du formulaire. Ajoutez-le, plus localhost pour les tests en local. Les sous-domaines d'un domaine listé sont autorisés.

Questions

Une étape vous bloque, ou vous voulez un guide pour votre cas ? Écrivez à hello@ikiform.com.