What Truncating an IP Address Actually Anonymises
Contents
Dropping the last octet is the standard answer to the IP question, and it is a reasonable one. What almost never follows is the second step: saying how large the group is that a visitor now disappears into. The measure is treated as a switch – masked or not masked – when it is a dial, and where the dial sits decides whether anything was achieved.
The numbers are small enough to do in one’s head, and they are worth having ready the next time someone asks whether the data is anonymous now. It usually is not, and that is not an argument against truncation – only against the sentence that follows it.

How Large the Remaining Group Really Is
Truncation works in bits, and each bit dropped doubles the number of addresses that share the stored value. Dropping a whole octet leaves 256, which sounds like a crowd until it is compared with what a single address actually covers.
| Stored | Addresses sharing it | What it still says |
|---|---|---|
203.0.113.45 |
1 | One connection – in a household, one household; in a company, one office. |
203.0.113.0 (/24) |
256 | Usually one provider block in one city, often one street or one building. |
203.0.0.0 (/16) |
65 536 | A region and a provider, no longer a place. |
| nothing | all of them | Nothing – and that is the only value that removes the reference. |
The middle row is where the argument usually happens. 256 addresses are not 256 people: a residential block from a provider is handed out to a few dozen connections at a time, and behind a corporate NAT the whole building leaves through one address. In one direction the group is smaller than the number suggests, in the other much larger – and neither is visible in the data itself.
That is why truncation is best described as what it is: a reduction of precision, sized in bits, that lowers risk without ending the personal reference. A /24, a timestamp to the second and a user agent are together narrow enough that supervisory authorities have treated the combination as personal data, and no amount of masking on the last octet changes that.
Why a Hash Is Not an Alternative
The suggestion arrives in every second discussion: store the hash instead of the address, then the value is unreadable. It is unreadable, and it is not anonymous – because the space it was drawn from is tiny.
IPv4 has about 4.3 billion addresses. A single ordinary machine computes SHA-256 in the order of hundreds of millions of hashes per second, so the complete table of every address and its hash is built in minutes and fits on a laptop. A hashed IPv4 address is therefore not a pseudonym; it is the address written in a different alphabet, and the lookup that undoes it is a rounding error in effort.
A per-event salt breaks the table, but it also breaks the only reason the value was kept: two events from the same visitor no longer match. A fixed salt keeps the joins and merely turns the table into a slightly more expensive one, computed once and reused for every record in the same dataset. What remains genuinely useful is a hash over the truncated address, where the group of 256 is what gets hashed – the protection comes from the truncation, not from the hash.
IPv6 Truncates Differently Than It Looks
The rule that was carried over from IPv4 – drop the last quarter – does almost nothing in IPv6, because the address is structured differently. A connection is usually handed a whole /64, and every device behind it invents its own suffix inside that block. Cutting an address down to its /64 therefore removes the device but keeps the connection, which is exactly the identifier that mattered.
Meaningful truncation starts one step further left. A /56 or /48 is what a provider assigns to a site, so that is where a group larger than one household begins. Google’s own rule for IPv6 – zeroing the last 80 bits, which leaves a /48 – sits at that point rather than at the visually equivalent quarter.
One case survives all of this unnoticed: an IPv4 address wrapped in IPv6 notation, written as ::ffff:203.0.113.45. Masking rules that count from the right leave the embedded address intact far longer than expected, and a value that looks like a masked IPv6 address can carry a complete IPv4 one.
Where in the Flow the Truncation Sits
Masking is only masking if it happens before the address leaves the perimeter, and that distinction separates two cases that are otherwise described in the same words.
In a server-side setup – a conversions API, a container on one’s own infrastructure – the address is a field in a payload. Whatever is written into client_ip_address is what the recipient learns, so truncating before the request is sent removes the last octet for good. This is the case where the measure does what it claims.
With a tag that runs in the browser, the situation is the reverse. The request goes to the vendor directly, and the address the vendor sees is the one on the connection, not the one in the payload. A masked value in a parameter changes what is stored in a report; it does not change what arrived at the edge. Only a proxy through one’s own domain moves that boundary, and then the address the vendor sees is the server’s.
What Truncation Is Actually For
The purpose is easier to defend once it is stated precisely. A /24 still resolves to a city and often to a district, which is the entire point: the geographic reporting that the address was collected for survives the cut almost unchanged, while the value that identifies a single connection is gone. The measure is well chosen for that trade, and poorly chosen for the claim that the data is now anonymous.
Documentation follows from the same sentence. A record of processing that states the mask length, the point in the flow where it is applied and the resulting group size describes something checkable. One that states “IP addresses are anonymised” describes a switch that does not exist, and the first question from anyone reviewing it will be the one this article started with: how large is the group.