Sitearc

A contact form for a static site, without a server

The endpoint contract and copyable markup. A filled post-launch check proves an enquiry was stored rather than merely sent.

Answers
Choose and verify a form endpoint for a static site.
Included markup
Copyable markup and a filled receipt check
Written for
Developers and owners of static sites

A static site has no server to post to

The page is a file. The form needs somewhere to send the answer, and that somewhere is an endpoint somebody else operates.

What the endpoint has to do

  1. Accept a POST from your origin, and refuse another origin.
  2. Store the response before returning success.
  3. Return a receipt naming the stored response.
  4. Attempt a notification separately from storing the answer.

Markup you can copy

<form action="https://app.example.com/f/form_8fa2/messages" method="post">
  <label for="name">Name</label>
  <input id="name" name="name" required />
  <label for="email">Email</label>
  <input id="email" name="email" type="email" required />
  <label for="message">What do you need?</label>
  <textarea id="message" name="message" required></textarea>
  <button type="submit">Send</button>
</form>

The filled post-launch check

Foxglove Bakery, one endpoint, checked on 2026-09-25.

endpoint          https://app.example.com/f/form_8fa2/messages
origin rule       foxglovebakery.example only
fields            name, email, message, and a hidden source field

checks
1. submit from the live page          receipt r_71aa returned
2. stored row                         present within one second
3. notification                       provider accepted in 40 seconds
4. submit from another origin         refused, as configured
5. visible in the inbox               yes, with the full message

issues found
- the notification went to an address the owner no longer uses
  fix: change the destination, then submit a second test
- no webhook signing secret stored
  fix: save it in the receiving service, not in the site source

Acceptance: the form is working when

  • a real submission returns a receipt id
  • the stored response is readable in the inbox
  • the notification is a separate row with its own state
  • another origin is refused
  • the confirmation tells the visitor the answer was received

Troubleshooting

Symptom Likely cause What to do
The form posts and nothing is stored The endpoint is misconfigured Check the response code and body
Stored, no email Delivery failed, or spam filtering Read the delivery state
Two notifications A retry duplicated Deduplicate by response id
Submissions refused from the live site The origin rule is wrong Add the production origin
The client never sees enquiries Nobody reads the inbox Add a second destination

Limits

A configuration screen is not proof. Only a stored response is.

Delivery is at least once, so a retry can repeat a notification. A receiving webhook should deduplicate by the response id.

Where this came from