A static site that collects form submissions, in one HTML attribute
A static site has no backend. That is the point of one — and it is also why the contact form is the first thing that breaks. The usual answers are a third-party form service with its own signup, a serverless function you now maintain, or a mailto: link nobody clicks. There is a third option that falls out of how static hosting already works: the host is in the path of every HTML response it serves. It can collect the form itself. On harvis.dev that is one attribute: <form harvis-form= "contact" > <input name= "email" type= "email" required > <textarea name= "message" ></textarea> <button> Send </button> </form> Deploy, and submissions show up in the dashboard. No script tag, no API key in the page, no fetch() , no JavaScript at all — the form works with JS disabled, because it is a plain HTML form doing what plain HTML forms have always done. The page I am describing is live at harvis-forms-example.harvis.dev — submit the form and see where you land. Everything below is what makes that page work. What actually happens The rewrite happens on the way out, while the HTML is being served: action and method are replaced with /__harvis/form/contact on your site's own subdomain. Same origin, so there is no CORS, no preflight, and nothing in the page has to know a project id. A honeypot field is inserted. It is positioned off-screen rather than display: none , because a bot that skips hidden inputs is a bot that would otherwise get through. Anything that fills it in gets the success page and is stored nowhere — a bot that can tell it was caught is a bot that tries again differently. data-harvis-redirect="/thanks.html" becomes a hidden field, since the handler never sees your HTML — only what the browser posts. It is re-validated on arrival, and a protocol-relative //somewhere-else is refused. The reply is a 303 , so the browser follows it with a GET and a refresh on the thank-you page cannot post the form twice. The form name is part of a URL and a dashboard heading, so it