Blog

Why We Stopped Putting Form Endpoints in Plain HTML

Generic spam bots don't read your JavaScript — they regex your HTML for a form action and replay junk to it. Here's the fix we shipped: hide the endpoint until validation passes.

← Back to the blog
AI Powered

Explore this article with AI

Get an instant summary, key takeaways, action items, and answers to your questions about this article.

Choose your AI assistant
ChatGPT Perplexity
Quick answer

A form's action="..." attribute is plaintext in your page source — a naive scraper can read it without running any JavaScript. We stopped putting the real endpoint there at all: the form now has no action attribute, and the URL exists only as a short encoded string inside the submit handler, decoded and used only after validation and reCAPTCHA both pass.

Most spam isn't sophisticated. It's a script that crawls the web, regex-parses every <form> tag it finds for an action attribute, and fires junk POST requests at whatever it discovers — across thousands of sites, with no browser and no JavaScript involved at all. Honeypots and reCAPTCHA are good defenses against bots that load your page. They do nothing against a bot that never loads it in the first place, because it already has your endpoint memorized from the raw HTML.

The tell was in our own data

When we dug into a spam spike on this site's own contact forms, the honeypot field was filled on effectively every spam submission, and select fields showed every possible option concatenated into one value — a dead giveaway that something was auto-filling every field it found, not a person clicking through a dropdown. That's the signature of a generic scraper, not a targeted attack. See our full guide to stopping form spam for the broader picture; this post is about one specific, underused fix.

The fix: never write the endpoint in plaintext

The change is small and doesn't touch how the form looks or behaves for a real visitor:

A visitor notices nothing. A scraper reading raw HTML finds no form action to replay against.

What this does and doesn't defend against

Be precise about the threat model here — this isn't a silver bullet. It stops bots that scrape static HTML without executing JavaScript, which describes the vast majority of mass, generic form spam. It does not stop a bot that already has your current endpoint hardcoded from an earlier scrape (the old URL is already "leaked" and needs rotating separately), and it won't stop a sophisticated, targeted attacker running a full headless browser. It's one more layer, not a replacement for honeypots or a real CAPTCHA — see reCAPTCHA vs. hCaptcha vs. Turnstile for how those compare.

Combine it with rotation for a clean break

Obfuscation alone doesn't help against a bot that already has your current endpoint. Pairing it with an occasional endpoint rotation — generating a fresh URL and swapping it in — invalidates every previously scraped target at once, while the obfuscation keeps the new one from being trivially harvested again. It's the same principle behind layered spam filtering: no single control does the whole job, but stacked together they close most of the gap.

Frequently asked

Does hiding the endpoint break anything for real visitors?

No — the form looks and behaves identically. The only difference is invisible: the destination URL is assembled in JavaScript memory at submit time instead of sitting in the page's static HTML.

Will this stop all form spam?

No. It specifically targets bots that scrape raw HTML without running JavaScript, which is the majority of generic spam. Determined, targeted attacks using a full headless browser can still see the real request once it's made.

Do I still need a honeypot or CAPTCHA if I do this?

Yes. This is a complementary layer, not a replacement — honeypots and CAPTCHA catch bots that do execute JavaScript, which this technique alone doesn't stop.

Key facts

  • Generic form-spam bots typically scrape static HTML for a form's action attribute and never execute JavaScript at all.
  • Removing the action attribute and only assembling the real endpoint in JavaScript after validation passes defeats that entire category of bot.
  • This technique doesn't stop bots that already have the current endpoint hardcoded from a prior scrape — rotating the endpoint is the complementary fix for that.

Terms in this post

Endpoint scraping
Automatically extracting a form's submission URL from a page's raw HTML, without loading or executing the page's JavaScript.
Endpoint rotation
Generating a new submission URL for a form and retiring the old one, invalidating any copies bots have already collected.

Get the next one in your inbox.

One email when we publish something worth reading. No spam — appropriately enough, we'd know.