← All essays

Who's filling out my contact form?

· Mason Walters

The other night my wife asked what I actually work on when I’m in the office late. I already had the code for this site pulled up, so I showed her: these lines in the editor turn into the website you see at masonwalters.io. (It was HTML. To be fair, I never told her it was a real programming language.)

Then I went to show off my favorite part. The contact form is wired to my Discord server, so when someone sends me a message, my phone buzzes. I typed a test message, hit submit, and, like any good demo, nothing happened.

I figured I had a bug. It turned out I just wasn’t signed in to Discord on my phone. When I logged in, I found a couple dozen messages I’d missed, and for a second I was worried. Then I was annoyed. I hadn’t missed anything important. It was worse than that: almost all of it was junk.

I’d recently added a little more defense to another project of mine, College Football Alum: a Turnstile check from Cloudflare. It’s free, and it’s built to stop exactly this kind of lazy spam, so I knew I should add it here too. All I’d need was a Cloudflare account, an API key, and—

“Is that it?” my wife asked.

I’d completely forgotten I was in the middle of a demo. I told her that yes, this is what I’m doing in my office late at night. She went off to bed with no better understanding of how I spend my time. But at least I had a project for the evening.

What showed up

Since the end of August, about two or three junk messages a week had been coming through the contact form. None came through the newsletter signup, just the contact form. And nearly all of it came from three “people.”

The prize giveaways. These were my favorite to read out loud.

THE LAMBORGHINI AVENTADOR IS THE PHENOMENAL BESTOWAL

Soar to a 2026 Rolls-Royce Phantom for $15,000

A $25,000 PROMO CODE TO LIGHT UP YOUR DAY

All signed “HarryDus,” all in caps, all linking to a telegra.ph page I did not click. If I’d known a Rolls-Royce was only $15,000, I’d have answered sooner.

The polyglot price shopper.

Subject: Hello, write about the price

Sveiki, aš norėjau sužinoti jūsų kainą.

That’s Lithuanian for “Hi, I wanted to know your price.” Over a few weeks, “RobertOxind” asked the same question in Lithuanian, Hawaiian, Icelandic, Danish, Hungarian, Azerbaijani, and Irish. I admire the commitment. I don’t sell anything, though. The closest thing this site has to a price list is the macro calculator, and it’s free.

The get-rich-quick newsletter.

Read the news and earn $50,000,000 per month handsomely

From “Edwardmunse.” Fifty million a month, handsomely. I’m still reading the news. Nothing yet.

Why it got through

The frustrating part is that the form already had defenses. They just weren’t the right ones for this.

The first was a honeypot: a form field that’s hidden from people, so a real person never fills it in, and anything that does is a bot. That works on the dumbest bots. These weren’t the dumbest bots. The software behind them knows to skip hidden fields.

The second was a rate limit: no more than five submissions a minute from the same visitor. That stops someone hammering the form. It does nothing about two or three posts a week.

Those names turned out to be the giveaway. “HarryDus” and “RobertOxind” are the fingerprint of off-the-shelf spam software that crawls the web and posts to any contact form it can find. Nobody was targeting me. My site was just on the list, like everyone else’s.

I didn’t figure that out by guessing. I pulled every submission from the database and read them. Once they were lined up next to each other, the pattern was obvious: the same three names, the same telegra.ph links, the same not-quite-English. It’s the same rule I try to follow with my training data. Look at what actually happened before you decide what to do about it.

What fixed it

Cloudflare Turnstile. It’s a CAPTCHA, but most people never see it. Cloudflare checks in the background whether the visitor looks like a person, and only shows a checkbox when it isn’t sure. No picking out traffic lights.

The part that matters happens on my end. When the form is submitted, the browser hands my server a token from Cloudflare. Before saving anything or pinging my phone, my server asks Cloudflare whether that token is real, whether it came from this form, and whether it came from my domain. A bot that posts straight to the form without loading the page has no token, so it gets turned away. Same for one that makes a token up.

If Cloudflare can’t be reached, the form rejects the message instead of letting it through. That’s a trade-off. During a Cloudflare outage, a real person couldn’t reach me through the form either. I’m fine with that. LinkedIn and X still work, and I’d rather miss a message for an hour than go back to reading about Lamborghinis.

For the colophon crowd: this site uses Turbo, which swaps pages without a full reload, and Cloudflare’s standard script tag doesn’t survive that well. So the widget loads through a small Stimulus controller that renders it when the form appears and cleans it up when you leave.

The bug I found along the way

The funny thing is, there really was a bug.

While testing the new check, I noticed what happened after a message went through. The form sent you to a page that didn’t exist. Your message was saved and it reached my phone, but the page you landed on was a 404. It had been that way since I built the form back in April. Everyone who wrote to me got thanked with an error page. If anyone saw it, nobody told me.

The fix was small: send people to an actual thank-you page instead of to an address that didn’t exist. I only found it because adding the spam check meant testing the whole path end to end, all the way to the page after “send.” I’d never done that before. I’d only ever checked that the message showed up in Discord.

Closing

None of this was personal. The spam was automated, which is almost reassuring. It also means “nobody would bother with my little site” isn’t a defense. Something will bother, because it bothers with everyone.

What I’d tell anyone with a contact form: read the junk before you pick a fix. The messages told me exactly what kind of bot I was dealing with, and that told me what would stop it.

I’ll check back in a month and see how quiet it’s been. Meanwhile, if you write to me through the contact form, you’ll get a real thank-you page now.


Found this useful or want to push back on it? Say hi.