Blocking an IP range sounds simple until a firewall refuses it. Most will not accept "everything from 203.0.113.40 to 203.0.113.90" — they want CIDR blocks, and that particular IP range is not one block but six. Some appliance and router interfaces do take a start and an end and convert it for you behind the scenes, though the moment you touch a server firewall, a web server config or a cloud rule you are writing prefixes yourself. That constraint is why the IP range in your logs almost never maps cleanly onto the CIDR rule you need to write.
Most people solve this IP range problem by rounding up. They see a range inside 203.0.113.x, write 203.0.113.0/24, and move on. That rule blocks 256 addresses to stop 51 — five times more than intended, and 205 of them belong to somebody else entirely.
This guide covers how to block a range of IP addresses properly: why one range splits into multiple CIDR blocks, how to work out the exact set, what over-blocking actually costs, the ranges you must never put in a blocklist at all, and why the same prefix length means something completely different in IPv6.
The rule I see pasted most often is a /16 that nobody meant to write. Somebody had trouble with a handful of addresses sharing the first two octets, reached for the shortest prefix that covered them all, and blocked 65,536 addresses to stop about nine. It works, in the sense that the nine are gone. It also removed an entire regional allocation, and the support tickets arrived three weeks later with no obvious connection to the change.
My rule before writing any IP range block is to say the number out loud. Not the prefix — the count. "I am blocking two hundred and fifty-six addresses" lands differently from "I am adding a /24", and it is the same sentence. Prefix notation is compact enough to hide its own blast radius, which is exactly why people write bigger rules than they intend.
Quick Answer: Blocking an IP Range
Convert your start and end addresses into the minimal set of CIDR blocks that covers the IP range exactly, then add each block to your firewall. An arbitrary range usually needs several of them, because every CIDR block must begin on a binary boundary. Do the conversion with a tool that turns a start and end address into blocks rather than by hand.
Why Can't You Just Enter a Start and End Address?
Because firewalls match on CIDR prefixes, not on arithmetic ranges. A CIDR block is defined by a fixed number of leading bits every address in it shares, so it has a fixed size and a fixed starting point and cannot begin wherever you like. A /24 must start on a multiple of 256, a /25 on a multiple of 128, a /26 on a multiple of 64, and so on down the scale.
That subnet notation is what every firewall speaks, while your range starts wherever the offender happened to be. When those two things do not line up, no single block fits, and the range has to be assembled from several. Wikipedia's own guidance for administrators writing range blocks opens with the same warning, because the alternative is blocking people who were never involved. If prefix notation itself is unfamiliar, our explainer on reading masks and slash notation covers the format; this guide is about what to do with it when you need a block rule.
| Prefix | Addresses Removed | Must Start On A Multiple Of |
|---|---|---|
| /32 | 1 | Any address |
| /30 | 4 | 4 |
| /28 | 16 | 16 |
| /26 | 64 | 64 |
| /24 | 256 | 256 |
| /20 | 4,096 | 4,096 |
| /16 | 65,536 | 65,536 |
| /8 | 16,777,216 | 16,777,216 |
That alignment rule is the whole explanation. Watching it play out on a real range makes it obvious.
How Does One Range Become Six Blocks?
The IP range conversion works greedily. Start at the first address, take the largest CIDR block that both starts there and stays inside your IP range, then repeat from wherever that block ended. Each step is constrained by two things at once: the alignment of the current position, and how much range is left. The result is the minimal CIDR set that covers your addresses exactly and nothing else.
203.0.113.40 to 203.0.113.90 — 51 addresses, 6 blocks
203.0.113.40/29 → 8 addresses
203.0.113.48/28 → 16 addresses
203.0.113.64/28 → 16 addresses
203.0.113.80/29 → 8 addresses
203.0.113.88/31 → 2 addresses
203.0.113.90/32 → 1 address
total: 51 — exactly the range, no more
Follow the first step and the logic falls out. Address .40 is a multiple of 8 but not of 16, so the largest block that can start there is a /29 covering .40 to .47. That lands on .48, which is a multiple of 16, so a /28 becomes possible. The blocks grow as alignment improves and shrink again at the end, where only a /31 and then a single /32 fit inside what remains.
One notation mistake shows up constantly in forum posts: writing the first address of your range with a prefix, as in 203.0.113.1/24, and expecting the block to start there. It does not. A /24 always begins on a multiple of 256, so that expression silently means 203.0.113.0/24 and covers everything from .0 upward. Strict parsers reject it outright with a "host bits set" error; lenient ones accept it and quietly change what you wrote.
The tail is where people give up and round. Those last three blocks cover eleven addresses between them, and writing them by hand feels absurd next to typing one /24. It is also the difference between a precise rule and a rule that removes two hundred strangers. Seeing the underlying bits makes the boundaries concrete — a binary view of any address shows exactly where each block can start.
Which raises the question the tail is really about: what does rounding up actually cost?
What Does Rounding Up to a /24 Really Cost?
Five times more than you intended, in the example above. The range holds 51 addresses while the /24 holds 256, so 205 addresses come out for no reason at all. On a residential or mobile allocation each one of those can represent a household or a shared carrier pool serving many people. The rule looks tidier on the page and the damage stays invisible, which is a bad combination.
The cost scales badly upward. Rounding a small range up to a /16 subnet removes 65,536 addresses, and Wikipedia's own guidance on range blocks warns administrators that a careless prefix can block tens of thousands or even millions of people who were never the problem. That warning exists because the mistake is common, not theoretical.
Say the count, not the prefix
Before any range rule goes live, state the number of addresses out loud. "Two hundred and fifty-six" and "a /24" are the same rule, but only one of them sounds like a decision. Every prefix shorter than /24 deserves a written reason, and every prefix shorter than /20 deserves someone else's agreement.
There is a legitimate exception. When you are deliberately removing a whole allocation — an entire hosting operator, for instance — a short prefix is correct rather than lazy, and the reasoning belongs in our guide to removing an operator by its network number. The failure mode here is different: reaching for a wide prefix because the precise one was inconvenient.
Precision has a cost of its own, though, and it shows up in the rule count.
Where Do You Actually Put the Blocks?
Every mainstream platform accepts CIDR notation, which is the good news for anyone blocking an IP range. Apache takes prefixes directly in a Require directive, Nginx accepts them in a deny line, cloud security groups expect them, and Cloudflare matches them inside an expression. Six blocks means six entries in most of those places, and that stays manageable right up until the list starts growing.
Same six blocks, three platforms
# Linux, one set rather than six rules
ipset add blocked_range 203.0.113.40/29
# Nginx
deny 203.0.113.40/29;
# Cloudflare expression
(ip.src in {203.0.113.40/29 203.0.113.48/28})
Three platform notes matter. A rule in your web server only protects that web server, so SSH, mail and anything else on the same machine stay reachable from the range you just blocked — which is the usual argument for putting range rules in the firewall instead. Apache also reads its per-directory file on every request, so a long list of prefixes there is a tax on your genuine visitors — the reasoning is in our guide to what server-level rules cost per request. And once you are past a few dozen entries on Linux, a set beats individual rules, because a set matches in constant time no matter how many prefixes it holds.
Lists also rot. Entries accumulate, overlap, and start contradicting each other.
How Do You Stop a Blocklist Contradicting Itself?
Check for overlap before you add any IP range to the list. A list built over months will contain blocks that sit inside other blocks, adjacent blocks that should be one larger block, and duplicates written by two different people. None of it breaks anything outright, which is precisely why it survives. It just inflates your rule count and makes the list impossible to reason about.
Two operations fix this. Aggregation merges adjacent CIDR blocks into fewer, larger ones where they align — two adjacent /29s starting on the right boundary become a single /28. Deduplication removes any block already contained inside another. Both shrink the list without changing what it covers, which is the only kind of optimisation worth trusting.
Aggregation is not rounding
Merging two adjacent /29s into a /28 changes nothing about coverage, because the /28 contains exactly those sixteen addresses and no others. Replacing them with a /24 covers 240 addresses you never listed. The first is arithmetic; the second is a decision disguised as tidying. Run the merge with an overlap and merge check so the distinction stays honest.
One category of entry causes more damage than any overlap, and it belongs in no blocklist at all.
Which Ranges Should Never Go in a Blocklist?
Anything that is not public internet space belongs nowhere near a block rule. Private and reserved ranges cannot reach you across the internet in the first place, so blocking them achieves nothing externally while breaking a great deal internally. A rule against 10.0.0.0/8 will happily cut off your own internal traffic, your monitoring, your container networking, and anything else living on a private network behind that same firewall.
| Range | Size | What It Is |
|---|---|---|
| 10.0.0.0/8 | 16,777,216 | RFC 1918 private — your own networks |
| 172.16.0.0/12 | 1,048,576 | RFC 1918 private — common in containers |
| 192.168.0.0/16 | 65,536 | RFC 1918 private — home and office LANs |
| 100.64.0.0/10 | 4,194,304 | RFC 6598 shared space — carrier NAT, not private |
| 127.0.0.0/8 | 16,777,216 | Loopback — your own machine |
| 169.254.0.0/16 | 65,536 | Link-local — what a host assigns when DHCP fails |
Row four is the one that catches people, because it looks like ordinary public space and is not. RFC 6598 reserved 100.64.0.0/10 for carrier-grade NAT, so addresses in it sit between subscriber routers and the provider's translation layer. It is neither private nor public, several overlay networks and cloud platforms now use it internally, and treating it as a normal range in either direction produces confusing results.
IPv4 at least behaves predictably once you know the rules. IPv6 does not transfer.
Why Doesn't IPv6 Work the Same Way?
Because the same number after the slash means something wildly different. An IPv4 /64 does not exist, while an IPv6 /64 holds over eighteen quintillion addresses and is routinely handed to a single customer. Prefix intuition built on IPv4 gives you nonsense in IPv6, and reusing an IPv4 habit is the fastest way to write a rule that is either useless or enormous.
Blocking a single IPv6 address is close to pointless for that reason. A visitor with a /64 has effectively unlimited addresses to move between, so a /128 rule stops one request and nothing more. The meaningful unit is the allocation, which usually means blocking at /64 for a single customer, or /48 and /56 where a provider hands out larger prefixes.
| IPv6 Prefix | Addresses | Typically Represents |
|---|---|---|
| /128 | 1 | One address a visitor can abandon instantly |
| /64 | 18.4 quintillion | One customer — the usual blocking unit |
| /56 | 256 × a /64 | One subscriber site with several networks |
| /48 | 65,536 × a /64 | A business allocation — treat with care |
The prefix numbers do not transfer
An IPv4 /24 is 256 addresses. An IPv6 /64 is 18,446,744,073,709,551,616. A /48 is larger again by a factor of 65,536. Reading an IPv6 prefix with IPv4 instincts will mislead you every time — our beginner's guide to how IPv6 addressing is structured covers why the scale is so different.
Whichever address family you are working in, one step separates a correct rule from an assumed one.
How Do You Verify the Blocks Cover What You Meant?
Test the boundaries of the IP range, not the middle. Any block will obviously match an address at its centre, so the useful checks are the address just before your range starts and the one just after it ends. If either matches, your set is too wide. If an address inside the range does not match, your set has a hole, which is the quieter and more common failure.
Six Checks Before A Range Rule Goes Live
1 Write down the exact first and last address
Take them from the logs, not from memory. Most over-blocking starts with a range nobody wrote down, because an unwritten range quietly expands to whatever prefix feels convenient.
2 Convert, then read the address count
Add up what the blocks cover and compare it to what you wrote in step one. The two numbers should be identical. If the total is larger, you have rounded somewhere without meaning to.
3 Check nothing private crept in
Scan the set for anything inside the reserved ranges above. A private block in a public blocklist does nothing useful and can cut your own infrastructure off from itself.
4 Test one address either side
Take the address immediately before your first and immediately after your last, and confirm neither falls inside any subnet in your set. Boundaries are where the arithmetic goes wrong, so boundaries are what you test.
5 Merge before you deploy
Run the set through an overlap check so adjacent blocks collapse and contained blocks disappear. Fewer entries, identical coverage, and a list the next person can actually read.
6 Record the count and a review date
Note how many addresses the rule removes and when to revisit it. Ranges get reassigned, and a block written against last year's occupant is now aimed at somebody innocent.
Step four is the one worth building a habit around. A set that covers your range correctly and also covers the address just outside it will behave perfectly in every test you think to run.
That is the whole method, from a pair of addresses to a rule you can defend. Here is the short form.
The Short Version
Convert your IP range into the minimal set of CIDR blocks rather than reaching for the nearest round prefix. CIDR blocks must start on binary boundaries, so an arbitrary IP range almost always needs several — the fifty-one addresses in our example need six. That looks like more work than one /24, and it is the difference between removing 51 addresses and removing 256.
Say the count out loud before you deploy. Prefix notation hides its own blast radius, and "a /16" sounds far smaller than "sixty-five thousand addresses" despite being the same rule. Keep private and reserved ranges out of the list entirely, including 100.64.0.0/10, which looks public and is not.
Treat IPv6 as a separate problem, because the same prefix number means something enormously different there and blocking a single address accomplishes nothing. Merge overlapping entries before deploying, test the addresses either side of your boundaries, and pick the layer deliberately — the full comparison of where a block can live works through that choice.
Block Exactly What You Meant
Turn a start and end address into the minimal CIDR set, then check whether a specific address really falls inside it. Two checks, no arithmetic, no accidental /24. Free, instant, no account.