A small forum for vintage radio enthusiasts woke up to find 4,000 new members overnight. None had posted yet. Over the next days the accounts began posting links to unrelated products.
Registration had no CAPTCHA and no email confirmation. Bots found the form and used it at scale, and the cleanup was a manual slog through a members list in alphabetical order.
The admin turned on email verification, added a challenge question only a radio enthusiast could answer, and approved new accounts by hand for their first post. The forum is a composite, and the numbers are examples, but anyone who has run a community site for a few years will recognise the morning.
The forum
It was a hobby site run by one retired engineer in the evenings. A few hundred regular members swapped schematics, valve substitutions and photos of sets rescued from lofts. It ran on free forum software on a modest shared hosting plan. Posting was friendly and slow: a good day brought thirty messages.
The admin had set up registration in the most welcoming way he could think of. Pick a username, type an email address and a password, press the button, and you were in. He disliked CAPTCHAs, having been defeated by several himself, and he saw no reason to ask new members to confirm an address. The community was small and polite and he knew most of the names.
That was a perfectly reasonable design for the forum of 2005. It was an open door in 2025.
The morning
He noticed first because of the member count in the footer. It had read 1,312 the previous evening. It now read 5,340. He assumed a bug in the counter, then saw the "newest member" line, a string of letters and digits he did not recognise, and the one before it, and the one before that.
The members list confirmed it. Thousands of accounts with names like a keyboard falling down stairs, each with a different email address at one of a few dozen unfamiliar domains, each with zero posts. The registration times were spread over about nine hours, a few a minute, at a steady pace that no human enthusiast would have kept up.
At that point nothing was visibly broken. The forum still loaded, the real threads were intact, and the regulars had noticed nothing. He left it, intending to look at it at the weekend. This is the usual mistake, and it cost him a week.
Why the bots wait
Over the following days the dormant accounts woke up. Threads began to attract replies that said nothing, then replies with a link to a shop selling something unrelated, then profile pages stuffed with adverts. Within a few days hundreds of posts a day were appearing, most in old threads that real members rarely read, which is why it took a while to be noticed.
The delay is deliberate. A new account that posts a link in its first minute is easy to catch. An account that has existed for a week and then posts looks a little more like a member. Search engines also see links in profile pages, and the people who run these operations are selling links, not messages. The more accounts and the older they are, the better the product.
The cleanup
His first attempt was to open the members list and delete accounts by hand. The list was sorted alphabetically by default, which scatters the junk among real members and makes the job nearly impossible. After an evening he had removed about two hundred and had a headache.
What worked was sorting by join date and acting on the time window. Real members joined at a trickle of one or two a week. The flood was a solid block of nine hours. In the forum's admin panel, and failing that directly in the database, he could select everything registered in that block that had no posts, which were all the dormant accounts, and remove them in a single operation. He took a backup first.
-- Example only: table and column names depend on your forum software
SELECT COUNT(*) FROM members
WHERE joined_at BETWEEN '2025-03-11 21:00:00' AND '2025-03-12 06:30:00'
AND post_count = 0;
DELETE FROM members
WHERE joined_at BETWEEN '2025-03-11 21:00:00' AND '2025-03-12 06:30:00'
AND post_count = 0;
The count first, the delete second, and a restore point before either. Accounts that had already posted spam were handled separately with the forum's own bulk tools, and the posts removed with them. A short list of the spam's link domains went into the word filter so that a repeat would be held for review.
Finding the traffic in the logs
The web server's access log told the same story from the other side. A quick count of registration requests by address showed which sources were responsible.
grep "POST /register" access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head
812 203.0.113.17
640 203.0.113.58
377 198.51.100.9
...
Blocking those addresses would have helped for a day. The attackers had hundreds of them and could switch freely, so he did not spend long on it. The log was more useful as evidence of what a human would never do: hundreds of registrations from a single address in a few hours, no preceding page views, no loading of the stylesheet. The troubleshooting guide has more on reading access logs.
The new registration process
He rebuilt the sign-up in layers, each of which he could explain to a member in a sentence.
- Email verification. A new account stays inactive until the owner clicks a link sent to the address given. Most bot addresses do not exist, so most accounts never activate.
- A challenge question on the form: "Which valve is the American equivalent of the EL84?" Anyone who knows the hobby answers in seconds, and no bot has the answer. He rotates the question twice a year.
- Moderation of the first post. A new member's first message waits for his approval; after one approved post the account posts freely.
- A hidden form field that humans never see and bots fill in, which marks the sign-up as spam.
One side effect to watch is outgoing mail. Verification messages sent to a pile of invented addresses bounce, and a high bounce rate can harm the sending reputation of the domain. Having correct SPF and DKIM records in place, and blocking sign-ups from throwaway email domains, keeps that small. The SPF builder is a quick way to check the first of those.
What each defence costs a real member
| Defence | Stops | Friction for a real person |
|---|---|---|
| Image CAPTCHA | Simple scripts | Medium, and poor for accessibility |
| Email verification | Fake or mistyped addresses | Low, one click |
| Topic question | Generic bots | Low for members, a barrier for others |
| Hidden field | Naive bots | None |
| First-post approval | Anything that got through | A short wait, once |
Six weeks on, the numbers had settled. A few spam sign-ups still arrive each day, mostly from people rather than scripts, and they stall at the first-post queue where he deletes them with one click. Real new members, about three a week, noticed the question and enjoyed it; two said it was the reason they stayed.
Checking it yourself
Look at your members list sorted by join date. A sudden spike is easy to see. Then look at the share of members with no posts and no logins after a month. A healthy small forum has some, but if it is the large majority, you have been collecting bots for a while. Check that your forum's outgoing mail works, that old unverified accounts expire, and that the registration page has at least two independent hurdles.
Loose ends
Is a CAPTCHA enough?
Not on its own. Services that solve them cheaply exist, and the dormant-account trick works either way. Layers help more than any single test.
Should I close registration entirely?
For a tiny community that is a fair choice: invite-only, or approve each request. It costs growth but not much else.
Will blocking countries or addresses work?
Only briefly. It can also block real members who travel or use a VPN.
Do these accounts harm the server?
Mostly through database size and moderation time. A large flood of requests can also slow a small plan, which the host may notice before you do.
What changed afterwards
An open registration form is an invitation, and the people who accept it are not the ones you had in mind. Add a few cheap hurdles that real members clear without thinking, hold the first post, and look at the member count now and then. When the flood does come, sort by date and remove it as a block rather than name by name.