Back in 2019 we cut over a news publisher to a new CDN origin cluster with fresh egress IPs, and within 36 hours their paywall API started rejecting us with 403s. We hadn't been compromised and we weren't sending spam — we'd just been assigned addresses that a previous tenant had burned running aggressive scrapers. That was my first real lesson that origin IP reputation isn't a set-and-forget asset; if you don't actively manage it, it'll take your origins offline for you.
How Clean IPs Get Flagged When Nothing Looks Broken
Most engineers assume an IP is clean if it passes a quick check on day one. I've made that mistake myself. The reality is that public cloud and even some dedicated hosting providers recycle addresses constantly, and blocklist operators don't always expire old listings quickly. You can request a pristine range, run it through Spamhaus, Barracuda, and SORBS, see all green, and still get blocked a week later because a downstream RBL you didn't check still associates that /24 with a crypto miner from six months ago. I now treat any new IP as guilty until proven innocent, with a mandatory warming and observation period before it touches production origins.
Even with a genuinely clean history, your own legitimate origin behavior can trash your reputation. CDNs are noisy by nature — health checks, cache revalidation, stale-while-revalidate traffic, and retry storms all look like aggressive scanning to an upstream WAF or API gateway. I once traced a sudden listing on AbuseIPDB back to our own Origin Health Monitoring Patterns: Active Probes, Passive Checks, and Hybrid Approaches that were misconfigured to probe every backend every second from eight PoPs. The target wasn't rate limiting us, they were just logging us as an abusive crawler. The HTTP Working Group Specifications are clear about how conditional requests should work, but many origin owners don't implement them strictly, so they see volume instead of intent.
The other silent killer is shared egress. When multiple customers or multiple services NAT behind the same handful of IPs, one noisy neighbor can poison the well for everyone. A marketing tool doing email validation, a scraper fetching product feeds, or even a misbehaving webhook retry loop can generate enough complaint volume to get the whole ASN flagged. That's why I've become pretty strict about isolating CDN source traffic from everything else. If your origin fetches, purge callbacks, and log pushes all share the same NAT gateway as your corporate browsing traffic, you can't control or even audit your reputation.
#!/bin/bash
# check-origin-reputation.sh - quick reputation check for new origin egress IPs
# Run this before promoting any new IP to production
ORIGIN_IPS=("203.0.113.10" "203.0.113.11" "198.51.100.45")
BLOCKLISTS=(
"zen.spamhaus.org"
"b.barracudacentral.org"
"dnsbl.sorbs.net"
"cbl.abuseat.org"
)
for ip in "${ORIGIN_IPS[@]}"; do
echo "=== Checking $ip ==="
rev=$(echo $ip | awk -F. '{print $4"."$3"."$2"."$1}')
for bl in "${BLOCKLISTS[@]}"; do
if dig +short "$rev.$bl" | grep -q "127.0.0"; then
echo " [LISTED] $bl"
else
echo " [OK] $bl"
fi
done
# Check AbuseIPDB score (requires API key)
score=$(curl -s -G https://api.abuseipdb.com/api/v2/check \
--data-urlencode "ipAddress=$ip" \
-H "Key: $ABUSEIPDB_KEY" | jq '.data.abuseConfidenceScore')
echo " AbuseIPDB score: $score/100"
done
Building an Egress Layer That Keeps Reputation Intact
After that 2019 incident, we stopped letting origin traffic use default cloud NATs. The pattern that's worked best for me is a dedicated, reputation-isolated egress tier just for CDN-to-origin traffic. Think small pools of elastic IPs, one pool per traffic class, with no other workloads allowed to use them. Your primary origin fetches get their own IPs, your health checks get another, and any background jobs like image optimization or log shipping live somewhere else entirely. That way if you need to rotate or remediate one pool, you don't have to touch the others. It's more expensive than a single shared gateway, but it's far cheaper than an emergency migration when you're blocked.
Connection behavior matters as much as IP isolation. Chatty origins that open a new TCP connection for every revalidation will not only hurt performance, they'll look abusive. I've had great results pairing dedicated egress with aggressive Origin Connection Pooling Optimization: Reducing TCP Overhead at CDN Scale — keeping persistent connections warm, reusing TLS sessions, and coalescing requests where you can. On the retry side, you also need discipline. An origin that returns 500s during a deploy shouldn't trigger a thousand instant retries from every edge node. Our standard now is to implement Origin Retry Strategies for CDN Resilience: Backoff, Jitter, and Budget Limits with exponential backoff and jitter at the egress, plus a global budget that caps retries per minute per IP. The folks over at web.dev Articles have solid guidance on how retry storms affect origin load, and the same logic applies directly to reputation — fewer, smarter retries look human; floods look like a botnet.
Finally, automate rotation before you need it. We keep a warm standby pool of IPs that have already passed reputation checks and have a neutral-to-positive sending history for 30 days. Terraform manages the pools, and a simple failover can shift traffic via route updates without DNS changes. Here's the kind of egress policy we enforce at the edge — nothing fancy, but it prevents accidental leakage of origin traffic through the wrong gateway:
# /etc/nftables/origin-egress.nft - enforce dedicated origin NAT
table ip origin_egress {
chain prerouting {
type nat hook prerouting priority -100; policy accept;
}
chain postrouting {
type nat hook postrouting priority 100; policy accept;
# Primary origin fetch pool - DO NOT use for other traffic
ip saddr 10.20.0.0/24 oifname "eth0" snat to 203.0.113.10-203.0.113.12 persistent
# Health check pool - isolated for easy allowlisting by partners
ip saddr 10.20.1.0/24 oifname "eth0" snat to 198.51.100.45 persistent
# Drop any origin-bound traffic trying to use the default NAT
ip saddr 10.20.0.0/16 oifname "eth1" drop
}
}
# Verify with:
# nft list ruleset | grep snat
# curl --interface 203.0.113.10 https://ifconfig.co
Signals You Should Watch Before Providers Start Blocking You
You don't want to learn about a reputation problem from a customer ticket. By the time an upstream provider blocks you outright, you've usually been on a downward trend for days. What I've learned to watch is the gray area — rising TCP connection resets, increasing 429s and 403s from origins that previously returned 200s, and subtle rises in TLS handshake failures. Those are often the first hints that a WAF or rate limiter has quietly deprioritized your IPs. We feed all origin response codes into a single dashboard and alert on rate-of-change, not just absolute thresholds, because a jump from 0.1% to 1.5% 403s is more telling than a steady 0.5%.
External reputation feeds are your early warning system, but you have to poll them continuously, not just at provisioning time. We run hourly checks against major DNSBLs and weekly checks against commercial threat intelligence feeds, and we track sender scores and AbuseIPDB confidence over time. If an IP's score slips from 0 to 15, that's not a crisis yet, but it's a reason to investigate what changed in your traffic pattern. I also recommend asking critical origin partners to allowlist your dedicated health check and fetch pools explicitly — most enterprise APIs will do this if you give them a stable, documented range, and it buys you a buffer while you remediate a listing elsewhere.
| Reputation Signal | Healthy Baseline | Warning Threshold | Action Required |
|---|---|---|---|
| AbuseIPDB Confidence Score | 0 - 5 / 100 | > 15 / 100 | Pause egress pool, audit logs for scraping or auth failures |
| DNSBL Listings (Spamhaus, Barracuda, SORBS) | 0 listings | 1+ listing on any major BL | Immediate rotation to standby pool + delisting request |
| Origin 403 / 429 Rate | < 0.3% of origin requests | > 1.0% sustained for 15 min | Throttle retries, check partner WAF blocks, open allowlist ticket |
| TLS Handshake Failure Rate | < 0.1% | > 0.5% to same origin ASN | Investigate JA3 fingerprint blocking or IP reputation filter |
| Spamhaus CSS / XBL Hits | 0 hits / week | 1 hit in 7 days | Review outbound connections for compromised host or open proxy |
Implementing a Reputation-Aware Egress Pipeline Without Breaking Origin Fetches
Alright, so you've got your theory down — now let's wire it up. The biggest mistake we see teams make is throwing all origin egress traffic through a single NAT gateway and calling it a day. That works until it doesn't. One misbehaving scraper workload, one compromised WordPress origin pulling malware lists, and that single /32 is toast across Spamhaus, Barracuda, and half the enterprise firewalls. You need segmented, observable egress.
What we do in production is split egress into pools by traffic class. Origin fetches (your CDN pulling from customers' origins) get a dedicated, low-churn pool of 3-5 Elastic IPs with a 30-day minimum age. Health checks, revalidation crawlers, and purge verification get a second pool. Anything user-generated or lower-trust gets a third, disposable pool that you can burn and rotate without impacting your core fetch reputation. Keep connection reuse above 85% and cap new outbound TCP connections to about 150-200 per second per IP — burstiness is what triggers rate-based heuristics faster than anything else. And if you're using Origin Request Hedging: Sending Parallel Requests to Cut Tail Latency, remember hedging multiplies that egress footprint. A 2-way hedge with a 95th percentile trigger effectively doubles your connection rate to that origin, so you must account for it in your per-IP limits.
Don't forget your timeouts and TLS posture either. Aggressive retries on a flaky origin look exactly like a SYN flood to an IDS. Tune your Origin Timeout Configuration Guide: Connect, Read, and Total Timeout Tuning so you're not hammering an origin with 3-second connect timeouts and 3 retries in 10 seconds — that's 3x the connection attempts for zero benefit. And enforce strict Origin SSL Verification at CDN Edges: Preventing Man-in-the-Middle Attacks on the way out. We've seen egress IPs get flagged because they were happily fetching through a hijacked DNS entry without verification, pulling malicious payloads that then got flagged downstream. Clean egress isn't just about *where* you connect from, it's *how* you connect.
#!/usr/bin/env python3
# egress-reputation-check.py - Run every 5m via cron, alert if listed
import dns.resolver
import ipaddress
EGRESS_IPS = ["203.0.113.10", "203.0.113.11", "203.0.113.12"]
DNSBLS = [
"zen.spamhaus.org",
"b.barracudacentral.org",
"bl.spamcop.net",
"dnsbl.sorbs.net"
]
def check_ip(ip):
reversed_ip = ".".join(reversed(ip.split(".")))
for bl in DNSBLS:
query = f"{reversed_ip}.{bl}"
try:
answers = dns.resolver.resolve(query, "A")
# If we get an answer, we're listed (127.0.0.x)
print(f"ALERT: {ip} LISTED on {bl} -> {[r.to_text() for r in answers]}")
return False
except dns.resolver.NXDOMAIN:
continue
except Exception as e:
print(f"WARN: check failed for {ip} on {bl}: {e}")
print(f"OK: {ip} clean on {len(DNSBLS)} blocklists")
return True
for ip in EGRESS_IPS:
check_ip(ip)
Advanced Patterns: Isolating Noisy Neighbors and Recovering Flagged Ranges
Even with perfect hygiene, you'll eventually get flagged. It happens. A customer points their origin to a domain that expires and gets parked with malware, your fetcher pulls it 400 times before anyone notices, and boom — VirusTotal lights up your egress IP. The question isn't how you avoid it forever, it's how fast you can isolate and recover without taking down fetch for everyone else.
The pattern that saves us most often is per-origin egress pinning with automatic quarantine. We hash the origin hostname to a primary egress IP, but we track per-IP error rates and DNSBL feedback in real time. If an IP's 5xx + timeout rate spikes past 12% over a 10-minute window, or we get a single DNSBL hit, that IP is automatically pulled from the active pool and all its assigned origins are re-hashed across the remaining clean IPs within 60 seconds. The flagged IP goes into a 48-hour quarantine where it only handles synthetic health checks — no customer traffic. This limits blast radius to about 1/N of your origins instead of 100%. For multi-tenant CDNs, we take it further: noisy origins (those returning >500 req/min of 4xx or with invalid certs) get shunted to the disposable pool after two strikes. Don't let one bad origin poison your premium pool.
Recovery is slower than you'd like — delisting from Spamhaus SBL can take 24-72 hours even with a clean remediation, and enterprise firewalls like Palo Alto or Fortinet cache those lists for 12-24 hours after delisting. While you wait, don't just NAT around the problem. File the delist request with logs showing the abusive origin is blocked (timestamp, host, your mitigation), publish a clean SPF/PTR record for the IP, and rotate traffic away completely for at least 7 days. We've seen teams rotate back after 24 hours and get immediately re-listed because the firewall never expired its cache. Keep that IP dark for a full week, let its reputation cool, then warm it back with <5% of traffic for another 3-4 days before reintroducing it fully.
Frequently Asked Questions
How many egress IPs do we actually need for origin fetches?
For most mid-size CDNs doing 5k-20k origin fetches per second, 3-5 dedicated IPs in your primary pool is the sweet spot. That gives you enough headroom to stay under 200 new connections/sec per IP and survive a single IP quarantine without overloading the rest. We rotate one IP out every 30-45 days to keep the pool age varied — you don't want all your IPs aging out at once.
Can we just use AWS/GCP NAT Gateway IPs and not worry about it?
No — that's the riskiest approach. Those gateway IPs are shared across their network and have no reputation guarantees. Plus you have zero control over PTR records, delisting, or isolation. We've seen a NAT Gateway IP flagged because another customer in the same AZ was scraping. Bring your own Elastic IPs or equivalent, set proper PTR (e.g., egress-01.fetch.yourcdn.net), and manage their reputation yourself.
Should we use different IPs for HTTP vs. HTTPS origin fetches?
You don't need to split by protocol, but you should split by trust level. Keep verified customer origins with valid certs on your premium pool, and put origins that fail SSL verification, have self-signed certs, or are newly onboarded (<7 days) on a separate greylist pool. That way a brand-new origin with a misconfigured cert doesn't drag down the reputation of your main fetch pool.
What's the first thing to check when origin pulls suddenly start failing at a specific customer firewall?
Check if your egress IP landed on a blocklist before you debug the fetch logic. Run the IP through MultiRBL and VirusTotal, check your PTR and that your egress isn't sending unexpected SMTP or DNS traffic, and ask the customer for their firewall vendor/logs. In about 70% of the "sudden origin unreachable" tickets we get, it's a reputation block, not a routing or timeout issue. Having that 5-minute DNSBL check script alerting you saves hours of misdirected debugging.