All articles

Block Botnet Traffic Before It Reaches Your Database

·5 min readbotnetddosip blockingapi securitybot mitigation

How IP-level filtering stops botnets, credential stuffing and scraped signups at the edge — before your app opens a database connection.

Most application stacks are built to be polite. A request arrives, the router hands it to a controller, the controller opens a database connection, runs a query, renders a response. That path is fine for humans. It is a gift for a botnet.

A distributed botnet does not need a vulnerability to hurt you. It only needs your application to keep saying yes: yes to the login form, yes to the search endpoint, yes to the password-reset email, yes to another connection from the pool. By the time the traffic reaches your ORM, you have already paid for TLS, session lookup, and a database round trip — multiplied by thousands of nodes per minute.

The cheapest request is the one you never process. That is the whole idea behind IP-level filtering at the edge.

What a botnet actually looks like at the IP layer

Botnet traffic rarely arrives from one address. It arrives from many, and the mix tends to fall into a few recognisable buckets:

  • Compromised residential hosts. Home routers, IoT devices and infected desktops. Individually low volume, collectively enormous.
  • Rented datacenter capacity. Cheap VPS instances spun up in bulk. A human customer almost never browses your checkout from an AWS, OVH or Hetzner range.
  • Commercial proxy networks. Residential and mobile proxy pools resold by the gigabyte, specifically marketed as a way around IP-based blocking.
  • Public VPN and TOR exits. Legitimate for privacy-conscious users, over-represented in abuse.

None of these are automatically malicious. A datacenter IP might be a monitoring probe you asked for. A VPN exit might be a customer on hotel Wi-Fi. That is exactly why the decision belongs in your hands and not in a blanket blocklist — you need the classification, then you apply your policy to it.

Blocking before the database, not after

The ordering matters more than the accuracy of any single signal. Consider two versions of the same login endpoint.

| Stage | Without IP filtering | With IP filtering first | | --- | --- | --- | | Parse request | Yes | Yes | | IP classification | — | ~1 lookup, cached | | Session / user lookup | Yes | Only if allowed | | Password hash verify | Yes (expensive by design) | Only if allowed | | Rate-limit write | Yes | Only if allowed |

Password hashing is deliberately slow — that is its job. A credential-stuffing run against an unprotected login turns your own security control into the denial-of-service vector. Moving the IP decision in front of it means an attacking node gets a 403 for the price of a hash-map hit, while your database connection pool stays free for real customers.

The same reasoning applies to search endpoints that hit full-text indexes, signup forms that trigger transactional email, and any endpoint that writes an audit row per attempt.

A practical policy, not a wall

Hard-blocking everything suspicious is how you end up with angry support tickets. A tiered response works better, and it is easy to express once you have a risk score and a type for each address:

  • Allow silently. Residential and business ISP addresses with a low risk score. The overwhelming majority of real traffic.
  • Add friction. VPN or TOR exits on sensitive actions only — signup, password reset, payout changes. Show a challenge, require email confirmation, skip the free-trial grant.
  • Throttle hard. Datacenter and hosting ranges hitting human-facing endpoints. Legitimate integrations belong on your API with a key, not on your login form.
  • Reject at the edge. Known abusive proxy infrastructure hammering authentication, or anything already over your per-IP budget.

Note that step 4 is the smallest bucket, not the default. The goal is to make abuse expensive, not to make your product unreachable.

Where the check belongs in your stack

Three placements, in rough order of effectiveness:

  • CDN or reverse proxy. The earliest possible point. The request never touches an application server. Best for blanket rules on obviously hostile ranges.
  • Middleware in your application. Runs before routing and before any database call. This is where nuanced, per-endpoint policy lives — strict on /login, relaxed on /pricing.
  • Inside the business logic. For decisions that need account context, such as flagging a login from a datacenter IP on an account that has only ever used one mobile carrier.

Most teams need the middle one first. It is a few lines in front of your router and it protects everything behind it.

Cache aggressively, fail open

Two operational rules keep an IP-reputation check from becoming its own outage:

Cache by address. Botnets reuse nodes. A short in-memory TTL — minutes, not days — collapses thousands of requests into a single lookup and keeps the added latency in the microsecond range for repeat offenders. Guarda caches lookups server-side for exactly this reason, and the cache window is configurable.

Fail open, log loudly. If the reputation service is unreachable, let the request through and record it. An anti-abuse control that takes your checkout down when a third party has a bad day has cost you more than the abuse would have. Reserve fail-closed for genuinely high-stakes actions like withdrawals.

What to measure afterwards

Turn the filter on in log-only mode first and compare a week of traffic against a week of blocking:

  • Requests rejected before reaching the database, as a share of total.
  • Database connection-pool saturation during peak abuse windows.
  • Failed-login attempts per hour.
  • Support tickets mentioning "can't log in" — the false-positive canary.
  • Signup-to-verified-account ratio, which usually improves sharply once disposable datacenter signups stop.

If the last two move in the wrong direction, loosen the tier rather than switching the whole thing off.

Honest limits

IP reputation is one layer, not a firewall. Residential proxy pools rotate through real consumer addresses that look indistinguishable from your customers. Carrier-grade NAT means one mobile IP can represent thousands of people. Attackers with budget will always find cleaner infrastructure than attackers without.

What IP filtering does reliably is remove the cheap, high-volume majority — the scripted floods running on rented servers and burnt proxy lists — so that your slower, smarter defences (device fingerprinting, behavioural analysis, MFA, rate limits scoped to accounts) only have to reason about a much smaller and more interesting population.

That is the realistic win: not an impenetrable perimeter, but a database that is still answering queries while you are under attack.

You can classify any address against Guarda's proxy, VPN, TOR, hosting and risk-score signals with the free check on the homepage, then wire the same call into the middleware in front of your routes.

Check an IP address now

Run a free proxy, VPN and risk check on any address, or plug the same data into your app through the API.