<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Himanshu Verma | DevOps | Full Stack Developer</title>
    <description>The latest articles on Forem by Himanshu Verma | DevOps | Full Stack Developer (@imhimanshu1stack).</description>
    <link>https://forem.com/imhimanshu1stack</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3810579%2F7731bdbb-3592-42c3-a4a3-ba26b81e83dc.jpeg</url>
      <title>Forem: Himanshu Verma | DevOps | Full Stack Developer</title>
      <link>https://forem.com/imhimanshu1stack</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/imhimanshu1stack"/>
    <language>en</language>
    <item>
      <title>Why 12% of Our Signups Were Fake — and What We Did About It</title>
      <dc:creator>Himanshu Verma | DevOps | Full Stack Developer</dc:creator>
      <pubDate>Fri, 06 Mar 2026 21:10:42 +0000</pubDate>
      <link>https://forem.com/imhimanshu1stack/why-12-of-our-signups-were-fake-and-what-we-did-about-it-1g05</link>
      <guid>https://forem.com/imhimanshu1stack/why-12-of-our-signups-were-fake-and-what-we-did-about-it-1g05</guid>
      <description>&lt;h2&gt;
  
  
  Why 12% of Our Signups Were Fake — and What We Did About It
&lt;/h2&gt;

&lt;p&gt;Last October, I opened our ESP dashboard and saw 12.3% hard bounces on onboarding emails.&lt;/p&gt;

&lt;p&gt;Not soft bounces. Not mailbox full. Hard 550 rejections. The kind that make your provider send you a warning email that feels like a threat.&lt;/p&gt;

&lt;p&gt;We weren't spamming anyone. We weren't scraping lists. These were users who had just signed up.&lt;/p&gt;

&lt;p&gt;That's when it clicked. This wasn't a sending problem. It was a signup problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Actually Happening When an Email Bounces
&lt;/h2&gt;

&lt;p&gt;Most developers don't look at the SMTP layer until something breaks. I didn't either.&lt;/p&gt;

&lt;p&gt;When your server sends mail, it connects to the recipient domain's MX record. The flow usually looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. TCP connect
2. EHLO your-domain.com
3. MAIL FROM:&amp;lt;you@your-domain.com&amp;gt;
4. RCPT TO:&amp;lt;user@example.com&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the mailbox doesn't exist, the remote server replies with something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;550 5.1.1 User unknown
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That rejection often happens before any message body is sent. No content. No HTML. Just a protocol-level no.&lt;/p&gt;

&lt;p&gt;If that happens often enough, your sending IP starts to look suspicious. Mail providers track this closely. A high hard-bounce rate tells them you don't control your data.&lt;/p&gt;

&lt;p&gt;And they don't like that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Regex and Double Opt-In Didn't Save Us
&lt;/h2&gt;

&lt;p&gt;Our first move was predictable.&lt;/p&gt;

&lt;p&gt;Regex validation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/^[^\s@]+@[^\s@]+\.[^\s@]+$/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It catches obvious garbage. It does nothing for &lt;a href="mailto:fakeuser@gmail.com"&gt;fakeuser@gmail.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Then we added an MX check. If a domain had no mail server, we rejected it. That removed some trash domains, but it still allowed addresses that didn't exist on real domains.&lt;/p&gt;

&lt;p&gt;We leaned on double opt-in next.&lt;/p&gt;

&lt;p&gt;Double opt-in helps, but it's reactive. You still send that first message. If it bounces, the damage is already done. Your reputation drops before the user ever clicks anything.&lt;/p&gt;

&lt;p&gt;We needed to stop bad addresses before they touched our database.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fix: Real-Time Mailbox Checks at Signup
&lt;/h2&gt;

&lt;p&gt;We decided to verify addresses at the moment of signup.&lt;/p&gt;

&lt;p&gt;Not just format validation. Not just domain existence. An actual mailbox check.&lt;/p&gt;

&lt;p&gt;We ended up using VerifiSaaS (&lt;a href="https://verifisaas.com" rel="noopener noreferrer"&gt;https://verifisaas.com&lt;/a&gt;). There are other tools out there like ZeroBounce and NeverBounce, but we wanted something API-first that fit directly into our backend without weird CSV workflows.&lt;/p&gt;

&lt;p&gt;Here's what our Next.js API route looks like now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// pages/api/signup.js&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.verifisaas.com/v1/verify&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;VERIFISAAS_API_KEY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;confidence_score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;classification&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;undeliverable&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;That email address does not exist.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;confidence_score&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;0.6&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;classification&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;risky&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Please use a valid email address.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it.&lt;/p&gt;

&lt;p&gt;The form posts to this route. We verify before writing anything to the database. If the address fails, we stop right there.&lt;/p&gt;

&lt;p&gt;No welcome email. No bounce. No reputation hit.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Changed After We Shipped It
&lt;/h2&gt;

&lt;p&gt;Within a week, our hard bounce rate dropped from around 12% to under 0.5%.&lt;/p&gt;

&lt;p&gt;That's roughly one in ten emails failing down to about four per thousand.&lt;/p&gt;

&lt;p&gt;Our ESP stopped sending warning emails. Our sender score climbed slowly over the next couple of weeks. Support tickets about missing onboarding emails dropped.&lt;/p&gt;

&lt;p&gt;The bigger win wasn't just reputation.&lt;/p&gt;

&lt;p&gt;Our database got cleaner. Marketing automation stopped choking on junk accounts. Analytics became more trustworthy. We weren't building features on top of fake users anymore.&lt;/p&gt;

&lt;p&gt;This should have been in v1.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Parts That Get Messy
&lt;/h2&gt;

&lt;p&gt;It's not all clean 250 or 550 responses.&lt;/p&gt;

&lt;p&gt;Some domains are catch-all. They accept any address during the SMTP check, then silently drop mail later. If you treat every 250 as valid, you'll overestimate quality.&lt;/p&gt;

&lt;p&gt;We don't auto-block catch-all domains. We flag them. For some users, that's fine. For others, especially high-risk signups, we ask for additional confirmation.&lt;/p&gt;

&lt;p&gt;Disposable providers are another grey area.&lt;/p&gt;

&lt;p&gt;For B2B SaaS, they're usually low intent. For consumer apps, they're sometimes legitimate privacy choices. We flag them and decide based on context instead of blocking blindly.&lt;/p&gt;

&lt;p&gt;And then there's performance.&lt;/p&gt;

&lt;p&gt;If your signup form suddenly gets hit with traffic spikes and you verify every address in real time, you'll feel it. We added basic rate limiting and a fallback mode if the verification API times out. The signup flow can't depend on a single external call without guardrails.&lt;/p&gt;

&lt;h2&gt;
  
  
  When This Is Worth It
&lt;/h2&gt;

&lt;p&gt;If you're sending a handful of emails a week, this probably doesn't matter.&lt;/p&gt;

&lt;p&gt;If you're running paid acquisition, onboarding thousands of users, or sending transactional mail tied to revenue, you can't afford double-digit bounce rates.&lt;/p&gt;

&lt;p&gt;We learned that the hard way.&lt;/p&gt;

&lt;p&gt;Fixing deliverability after your domain reputation drops is painful. Preventing bad data at the edge is much easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd Do Differently
&lt;/h2&gt;

&lt;p&gt;I would have treated signup validation as part of email infrastructure from day one.&lt;/p&gt;

&lt;p&gt;We thought email issues lived in the sending layer. They didn't. They lived in the input layer.&lt;/p&gt;

&lt;p&gt;Bad data spreads fast. It contaminates analytics, marketing, billing, and support workflows. Once it's in, cleaning it up is tedious.&lt;/p&gt;

&lt;p&gt;Stopping it at the door is boring engineering work.&lt;/p&gt;

&lt;p&gt;But it works.&lt;/p&gt;

</description>
      <category>saas</category>
      <category>node</category>
      <category>startup</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
