Deliverability13 min read

Inbox Placement Red Flags: 8 Technical Patterns That Trigger Gmail and Outlook Spam Filters

Even with perfect SPF/DKIM/DMARC, these 8 technical patterns trigger spam filters: suspicious links, HTML-text imbalance, attachments, sender inconsistency, urgency language, authentication alignment, volume spikes, and missing unsubscribe mechanisms.

R

React Emails Pro

February 28, 2026

Your SPF, DKIM, and DMARC are green. Your sender reputation looks fine. But your password resets are still landing in spam.

Modern spam filters don't just check authentication — they pattern-match against thousands of signals. This is a field guide to the technical red flags that make Gmail and Outlook suspicious, even when your authentication is perfect.

These aren't absolute rules — they're risk signals. Stack enough of them and you tip from inbox to spam, even with clean DNS records.

Why authentication isn't enough

SPF/DKIM/DMARC prove identity — that you're allowed to send as your domain. They don't prove trustworthiness.

Inbox providers use machine learning models trained on billions of emails. They know what legitimate transactional email looks like — and what phishing, scams, and low-quality bulk mail look like. When your email matches the wrong pattern, authentication won't save you.


Links are the #1 phishing vector. Filters inspect every linkin your email for common scam patterns.

What triggers filters:

  • Link text mismatch: Display text says "example.com" but href points to "sketchy-domain.xyz"
  • URL shorteners: bit.ly, tinyurl, t.co — anything that hides the real destination
  • IP addresses in links: http://192.168.1.100/reset instead of a proper domain
  • Misleading anchor text: "Click here", "Verify now", "Urgent action required" (classic phishing language)
  • Too many domains: Links to 5+ different domains in one email (looks like spam aggregation)

Safe alternatives:

password-reset-link.html
<!-- ❌ Triggers filters -->
<a href="https://bit.ly/abc123">Reset your password</a>
<a href="https://yourapp.com/reset">Click here to reset</a>

<!-- ✅ Safe pattern -->
<a href="https://yourapp.com/reset?token=...">
  Reset your password at yourapp.com
</a>
Use one primary domain for all links in transactional email. If you need tracking pixels or analytics, use the same domain with different paths — not third-party tracking domains.

2. HTML-to-text ratio problems

Spammers hide keyword-stuffing and sketchy content in HTML comments, hidden divs, or white-on-white text. Filters look for unusual ratios between visible text and HTML markup.

What looks suspicious:

  • Excessive markup: 10KB of HTML for 3 sentences of text
  • Hidden content: display:none, visibility:hidden, or font-size:0 text blocks
  • Image-only emails: No meaningful text, just one big image with a link
  • Missing plain-text version: HTML-only emails with no text/plain MIME part

Safe structure:

send-email.ts
import { render } from "@react-email/render";
import { Resend } from "resend";
import PasswordResetEmail from "./emails/password-reset";

const resend = new Resend(process.env.RESEND_API_KEY);

const html = await render(<PasswordResetEmail resetLink={link} />);
const text = await render(<PasswordResetEmail resetLink={link} />, {
  plainText: true, // Generate clean plain text version
});

await resend.emails.send({
  from: "security@yourapp.com",
  to: user.email,
  subject: "Password reset requested",
  html,
  text, // Always include both
});
Always send both HTML and plain-text versions. Many email clients prefer plain text, and filters penalize HTML-only messages.

3. Attachment red flags

Attachments are another phishing vector. Most transactional emails don't need attachments — and when you send them, filters scrutinize them heavily.

High-risk attachment types:

  • Executable files: .exe, .bat, .sh, .app, .dmg
  • Scripts: .js, .vbs, .ps1, .scr
  • Compressed archives: .zip, .rar, .7z (especially if they contain executables)
  • Office macros: .docm, .xlsm (macro-enabled documents)
  • Suspicious PDFs: PDFs with embedded JavaScript or forms that request personal info

Safer approach:

Instead of attaching files, host them securely and link to download pages:

invoice-email.tsx
// ❌ High-risk: PDF attachment
await resend.emails.send({
  attachments: [{ filename: "invoice.pdf", content: pdfBuffer }],
});

// ✅ Lower-risk: Secure download link
const downloadUrl = `https://yourapp.com/invoices/${invoiceId}/download`;

<P>
  Your invoice is ready. <a href={downloadUrl}>Download invoice #{invoiceId}</a>
</P>
If you must send attachments: stick to safe formats (.pdf for documents, .png/.jpg for images), keep file sizes small (<1MB), and use descriptive filenames (invoice-2026-02.pdf, not document.pdf).

4. Sender identity inconsistency

Filters track sender patterns over time. Sudden changes in how you identify yourself look suspicious.

Inconsistencies to avoid:

  • Changing From address frequently: notifications@yourapp.com one day, support@yourapp.com the next, noreply@yourapp.com after that
  • Mismatched display names: "YourApp Security" in one email, "Customer Support" in another, "YourApp Team" in a third
  • Domain hopping: Sending from different domains or subdomains without warm-up
  • Reply-To mismatch: From is automated@domain.com but Reply-To is a completely different service

Consistent sender identity:

email-config.ts
// ✅ Pick ONE sender config and stick to it
export const EMAIL_CONFIG = {
  from: "notifications@yourapp.com",
  fromName: "YourApp",
  replyTo: "support@yourapp.com", // Optional, keep consistent
} as const;

// Use everywhere
await resend.emails.send({
  from: `${EMAIL_CONFIG.fromName} <${EMAIL_CONFIG.from}>`,
  replyTo: EMAIL_CONFIG.replyTo,
  // ...
});
Establish one sender identity per email type and keep it stable. If you need to change, do it gradually and maintain the old sender for at least 30 days during transition.

5. Urgency manipulation language

Phishing emails rely on urgency and fear to bypass rational thinking. Filters are trained to detect manipulative language patterns.

High-risk phrases:

  • "Urgent action required immediately"
  • "Your account will be suspended"
  • "Verify your identity now or lose access"
  • "Unusual activity detected - click here"
  • "You have won / You have been selected"
  • "Act now or expire"
  • ALL CAPS SUBJECT LINES
  • Multiple exclamation marks!!!

Calm, specific alternatives:

subject-lines.ts
// ❌ Triggers spam filters
subject: "URGENT: Your Account Will Be Deleted!!!"
subject: "Action Required Immediately - Verify Now"

// ✅ Calm, specific, helpful
subject: "Password reset requested for your account"
subject: "Verify your email to complete signup"
subject: "Invoice #12847 from YourApp (Feb 2026)"

In email copy, prefer calm explanations over manufactured urgency:

password-reset-email.tsx
// ❌ Manipulative tone
<P>
  URGENT: Your password reset link expires in 1 hour! 
  Click now or lose access forever!
</P>

// ✅ Calm, informative
<P>
  You requested a password reset. This link is valid for 1 hour 
  for security reasons. If you didn't request this, you can safely 
  ignore this email.
</P>
Real security events should still be communicated clearly — just avoid theatrical language that mimics phishing tactics. Be direct, specific, and helpful.

6. Authentication alignment failures

Even with SPF and DKIM configured, misalignment between From,Return-Path, and DKIM domain can trigger filters.

What alignment means:

  • SPF alignment: The domain in From matches the domain in Return-Path (or they share the same organizational domain)
  • DKIM alignment: The DKIM signature domain (d=) matches the From domain

Common misalignment scenario:

email-headers.txt
# ❌ Misaligned - looks suspicious
From: notifications@yourapp.com
Return-Path: bounce-12345@sendgrid.net
DKIM-Signature: d=sendgrid.net

# ✅ Aligned - passes DMARC
From: notifications@yourapp.com
Return-Path: bounce-12345@mail.yourapp.com
DKIM-Signature: d=yourapp.com

How to fix alignment:

  1. Use a custom sending domain with your email provider (Resend, Postmark, SendGrid, etc.)
  2. Configure DKIM to sign with your domain, not the provider's
  3. Set up a MAIL FROM subdomain that matches your organizational domain
  4. Check mail-tester.com to verify alignment is correct
Most reputable email providers (Resend, Postmark, AWS SES) make alignment easy with custom domain configuration. If you're using a shared provider domain, upgrade to a custom domain.

7. Sudden volume spikes without warm-up

Inbox providers track sending patterns. A domain that sends 10 emails per day suddenly sending 10,000 looks like a compromised account or spam operation.

What triggers volume filters:

  • Going from 0 to thousands of emails per day overnight
  • Irregular bursts (100 emails Monday, 5 Tuesday, 500 Wednesday)
  • Launching a new product with a massive email blast from a cold domain
  • Sending from a new IP address without gradually ramping volume

Safe volume ramp (warm-up):

  1. Week 1: 50-100 emails/day to engaged users
  2. Week 2: 200-500 emails/day
  3. Week 3: 1,000-2,000 emails/day
  4. Week 4+: Gradually scale to target volume
If you're launching a new product: start sending transactional emails before you launch. Send test emails, internal team emails, beta user invitations — anything to establish a sending history before the flood of signups.

8. Missing or broken unsubscribe mechanisms

Even transactional emails benefit from clear opt-out mechanisms. Filters know that legitimate senders make it easy to unsubscribe — scammers don't.

What filters look for:

  • List-Unsubscribe header: RFC 8058 one-click unsubscribe support
  • Visible unsubscribe link: Clear, accessible footer link
  • Working mechanism: Links that actually unsubscribe, not broken 404 pages
  • Respect preferences quickly: Process opt-outs within 48 hours

Implementation:

email-with-unsubscribe.ts
await resend.emails.send({
  from: "notifications@yourapp.com",
  to: user.email,
  subject: "Your weekly summary",
  html,
  headers: {
    // One-click unsubscribe (Gmail prominently displays this)
    "List-Unsubscribe": `<https://yourapp.com/unsubscribe?token=${token}>`,
    "List-Unsubscribe-Post": "List-Unsubscribe=One-Click",
  },
});

And in your email template footer:

email-footer.tsx
<P style={{ fontSize: "12px", color: "#666" }}>
  Don't want these emails? {" "}
  <a href={`https://yourapp.com/unsubscribe?token=${token}`}>
    Unsubscribe
  </a>
</P>
For purely transactional emails (password resets, receipts), unsubscribe isn't required by law — but including a preference center link ("Manage email preferences") still helps deliverability.

Testing and monitoring

After fixing red flags, validate that your emails are actually landing in the inbox:

Testing tools:

  • mail-tester.com: Comprehensive spam score analysis (free, instant)
  • Google Postmaster Tools: Domain reputation monitoring for Gmail
  • Microsoft SNDS: Sender reputation data for Outlook
  • Your email provider's dashboard: Bounce rate, complaint rate, delivery stats

Healthy metrics:

  • Bounce rate: <5% (ideally <2%)
  • Complaint rate: <0.1% (people marking as spam)
  • Open rate (transactional): 40-70% (for emails users expect)
  • Spam score (mail-tester): 8.0+ / 10
If your bounce rate suddenly spikes or complaint rate exceeds 0.3%, investigate immediately. High complaint rates can permanently damage your sender reputation.

Quick audit checklist

Run through this checklist before launching email flows:

  1. Links: All use your primary domain, no URL shorteners, descriptive anchor text
  2. Content balance: Both HTML and plain-text versions, reasonable markup-to-text ratio
  3. Attachments: Avoid if possible; if required, use safe formats and host securely instead
  4. Sender identity: Consistent From address, display name, and Reply-To across all emails
  5. Language: Calm, specific tone; no urgency manipulation or phishing clichés
  6. Authentication: SPF, DKIM, DMARC aligned with your From domain
  7. Volume: Gradual ramp-up if launching new flows; consistent sending patterns
  8. Unsubscribe: List-Unsubscribe header + visible footer link, working opt-out flow

When good emails still land in spam

If you've fixed everything and emails still get filtered:

  • User behavior matters: If recipients consistently mark your emails as spam or don't open them, filters learn to route future emails to spam
  • IP/domain reputation is historical: If your domain or IP was previously abused, you're starting from negative reputation
  • Shared IP reputation: If you're on a shared sending IP, other users' behavior can affect you (consider dedicated IP if sending high volume)
  • Recipient settings: Some users have aggressive personal spam filters or corporate security policies

Focus on user engagement: send emails people want, at times they expect them, with content that's genuinely useful. Technical fixes get you 80% of the way — the last 20% is earned through user trust.

Production-ready templates for every flow

Pick from 9 template packs built with React Email. One-time purchase, lifetime updates, tested across every major email client.

Browse all templates