---
title: "When Your Own Firewall Locks You Out"
canonical: https://dxdev.com/blog/2026-06-15_firewall-self-lockout/
datePublished: 2026-06-15
---
The office lost access to its own Staff Center at 11:38 AM. Not a customer, not a hosting client. Us.

The cause was a local page-scanning macro someone had running against our own sites. It crossed 1,500 fast-hits/day, and our own IPFilter did exactly what it was built to do: it blacklisted the source IP. That IP happened to be the office's. So the tool we built to keep bad traffic out took out the whole office, including the internal admin panel we needed to fix it.

## The filter was correct and that was the problem

IPFilter isn't naive. It watches request rate per IP and bans anything that crosses a threshold, because that's what scrapers and credential-stuffing bots look like from the server's side. 1,500 hits in a day from a single address is a reasonable bot signal. The rule fired exactly as designed.

What the rule had no concept of was *whose* IP it was looking at. There was no allowlist, no tag for "this address belongs to staff," no distinction between a scraper hammering a login form and someone running an internal QA scan against the same sites we host. The filter only had one dimension: request volume per IP. Once that number crossed the line, the enforcement was identical regardless of source.

That's the failure mode worth naming: an automated defense with a single signal will eventually treat your own tooling as the attack it's designed to catch. It's not a bug in the threshold. 1,500/day is a fine number. It's a missing dimension in the model.

## Diagnosing it from outside your own perimeter

The report came in as "I'm getting IP blocked by our IP blocker, this is urgent." First check was whether it was actually IPFilter or something upstream at Cloudflare, since we'd been mid-migration on a separate epic that same day and DNS/proxy issues were already on the radar. Ruled that out fast: the block was local, and traffic wasn't even reaching Cloudflare-fronted domains that hadn't migrated yet.

Next was finding the actual record of the ban. IPFilter's state lives in the leaguecache database, not in a log file you tail, so the fix path was: SSH into the box, query leaguecache directly for the office IP's ban entry, and confirm the trigger reason and hit count matched the macro's timing. It did. Cleared the ban row, then went a step further and added the office dev IP to a permanent allowlist entry directly in that same database, so the same macro run can't re-trip it later.

Then verified from the outside: loaded the site from the actual office IP, not from a VPN or a staging box, to confirm the block was really gone and not just stale in some cache layer.

## The alternative that lost: turn off the scanning macro

The easy fix on the table was "just don't run the macro that hits 1,500 pages a day." That solves this specific incident but it's treating the symptom. The macro is legitimate internal tooling doing a legitimate job at a legitimate volume; the fact that it happens to look statistically identical to an attacker is the actual defect. If we throttle the macro instead of fixing the filter, the next piece of internal tooling that scales up traffic (a new monitoring script, a bulk migration check, anything hitting more than a handful of pages per minute) walks into the exact same trap, and we're back here with a different urgent message.

So the allowlist entry, not a rate-limit tweak on the macro, is the actual fix. It encodes the missing dimension: known-internal-source overrides volume-based enforcement for that address. The threshold logic stays untouched for everything else.

## The general shape

Any automated gate that acts on a single signal, request rate, error rate, failed-login count, whatever, will eventually score your own infrastructure as the threat, because your own infrastructure is the heaviest, most consistent user of the system it's watching. The fix isn't loosening the threshold. It's giving the system a way to know what it already trusts, before it decides what to block. We didn't have that carve-out for internal IPs going into this incident. We do now, and the next scanning tool we build against our own sites won't be the thing that locks us out of them.
