How to collect HTML form submissions without PHP
Point a static HTML form at an HTTPS endpoint — no PHP mail(), no server of your own, and spam controls included.
Shared hosts used to make this easy: drop a mail.php next to your site, point action at it, and emails arrived. Static hosts (GitHub Pages, Cloudflare Pages, Netlify, S3, many CDNs) do not run PHP. The form still needs somewhere honest to POST — an HTTPS HTML form backend that stores the submission and keeps bots out.
Yes — you can collect HTML form submissions without PHP by posting the form directly to an HTTPS endpoint built for static sites. The important pieces are a public form endpoint, domain checks, spam controls, and export.
What people try instead
mailto:actions. Unreliable, expose your address, and break on half of mobile browsers.- A one-off Worker, Lambda, or CGI script. Works until you own rate limits, domain checks, spam, and exports forever.
- Heavy ESP form embeds. Fine for full marketing automation; overkill when you only need an email and a clean list.
- Host-bundled form products. Convenient until you change hosts or want the data outside that vendor.
What a static HTML form endpoint needs
- An HTTPS URL that accepts
POSTas form-urlencoded or JSON. - A way to lock submits to your site domains (not every origin on the internet).
- Baseline spam controls: rate limits, a honeypot, optional bot challenge.
- Exportable storage so you can move the list into a newsletter tool later.
Pattern: HTML form submissions without a PHP backend
Simple Signups is that slice: one public campaign id, domain allow-list, and a single subscribe URL. Create a campaign in the dashboard, add the domains that will embed the form, then POST from plain HTML — no SDK and no server of your own. Full field reference lives in the developer docs; product-shaped contact and newsletter flows are under contact forms and newsletter signups.
Working HTML example
Replace the campaign id (or use the control on the snippet when you are signed in). Leave the honeypot hp empty — filled values are dropped. See the quickstart and endpoint sections for every field.
<form action="https://simple-signups.com/api/subscribe" method="POST"> <input type="hidden" name="campaignId" value="pub_your_campaign_id" /> <input type="email" name="email" required placeholder="you@example.com" /> <input type="text" name="hp" tabindex="-1" autocomplete="off" style="position:absolute;left:-9999px" aria-hidden="true" /> <button type="submit">Subscribe</button></form>Same endpoint with cURL
Handy for a smoke test from your laptop before you wire the page:
curl -X POST https://simple-signups.com/api/subscribe \ -H 'content-type: application/json' \ -d '{"campaignId":"pub_your_campaign_id","email":"jane@example.com","tags":["newsletter"]}'Path-style URL (optional)
Prefer the campaign id in the path? POST /c/:publicCampaignId only needs email in the body (a matching campaignId field is allowed). Documented under path-style endpoint.
curl -X POST https://simple-signups.com/c/pub_your_campaign_id \ -H 'content-type: application/json' \ -d '{"email":"jane@example.com","tags":["newsletter"]}'Anti-abuse without running PHP
Submissions are checked against the campaign domain allow-list, per-IP and per-campaign rate limits, the honeypot field, and optional Cloudflare Turnstile when you turn it on. Details stay in the docs — the form above already includes the honeypot pattern the API expects.
Related guides
- Contact forms use case — names plus email on a marketing site.
- Newsletter signups — list growth with optional double opt-in.
- Developer docs — fields, errors, and anti-abuse.