Anticode Open builder

Forms post to your own site: the check that keeps submissions at home

A form's action attribute decides where everything a visitor types is sent. On an Anticode page, every form must use method="post" and an action that's a plain path on the site itself beginning /api/. Sentinel checks this in the builder, and the server checks it again before publishing, so a form can't quietly send names and emails to someone else's server.

Updated · By the Anticode team

How a form ends up sending data elsewhere

Nothing in HTML stops a form on one site submitting to another. That's useful, and it's also how data leaks. Code generators and templates often wire forms to whatever service appeared in their training data: a form-handling service, a sample endpoint, an address that belonged to someone else's demo. The page looks right, and every enquiry goes to a server its owner has never heard of. An injected form, or a change made in a hurry, can do the same on purpose.

There's a quieter variant too: method="get". A GET form puts every field into the address of the next page, so names, emails and messages end up in browser history, server logs and analytics, and can travel in the referrer to the next site the visitor opens.

What passes and what doesn't

Form targets and how the check treats them
FormResult
method="post" action="/api/contact"Passes
method="post" action="/api/fn/quote"Passes: a custom form for a site function
method="get" action="/api/contact"Fails: forms must POST
method="post" action="https://forms.example.net/x"Fails: another origin
method="post" action="//elsewhere.net/api/x"Fails: a protocol-relative address is another host
method="post" action="/api/../admin"Fails: only plain path segments are allowed
A button with formaction pointing elsewhereFails: a button can't override the form's target

Put shortly: form action, own origin. The path rule is precise: /api/ followed by one or more segments of letters, numbers, hyphens and underscores, with an optional closing slash. No dots, no doubled slashes, no host. In the builder, Sentinel reports “Forms post to your own origin” with the number of forms checked. Its version also fails a page if any element carries a formaction attribute at all.

The server's check at publish

The builder's scan runs in your browser. When you publish, the server parses each page with a real HTML tokenizer, decodes character references in the action the way a browser would, and applies the same rule to every form and to every formaction on a button. If one fails, publishing stops and the message quotes the method and target it found, for example a form using GET to a given address, with the reminder that forms must POST to a /api/ path on this site. That re-check is what makes the rule hold for pages changed by the CLI, an agent or an AI assistant, not just the builder.

What happens when a form arrives

Posting to the site's own origin is half the arrangement. The other half is what that origin does with the post. Anticode's server only accepts a form whose request says it came from the same site, rate-limits it per visitor and per site, validates the fields, and stores the submission sealed. From there it can email the owner and go to the account's form connectors, such as Slack, Google Sheets or a webhook. So when data does leave Anticode, it goes where the owner chose, after it has been checked, rather than where a template happened to point. The built-in forms use /api/contact, /api/bookings, /api/subscribe and /api/auth/magic-link for the invite form; custom forms use /api/fn/ followed by the function's name.

Seeing where any form submits

On any site, right-click a form field, choose Inspect, and look at the form element above it for action and method. Check the submit button for a formaction too. If the action is a full address on another domain, that's where the data goes. The guide on stopping contact form spam covers the other side of form safety.

Limits

  • Page builds only. App builds can't post forms anywhere: they send through Anticode's form pipeline instead, checked by the app code gate.
  • Your own endpoint, by design. If you want submissions to reach another service, use a connector or a signed site function, not a form that posts there directly.

Questions people ask

Can a form on my Anticode site post directly to another service?

No. The form action must be on its own origin under /api/. To send submissions on, use a form connector or a webhook site function.

Why must forms use POST?

A GET form puts the visitor's answers in the page address, where they end up in history and logs. POST sends them in the request body.

What is formaction?

An attribute on a submit button that overrides the form's action. Anticode treats it like any other form target, so it can't point elsewhere.