Python ipaddress: IPv4 and IPv6

Published on: August 18, 2026
Reading time: 5 minutes
A laptop screen showing a code editor with visible programming code in a dimly lit environment.

The Python ipaddress module creates, validates, and manipulates IPv4 addresses, IPv6 addresses, CIDR networks, and interfaces. It can test membership, calculate network and broadcast addresses, split blocks, summarize ranges, classify special addresses, and convert between text, integer, and packed-byte forms.

The library does not open sockets, resolve DNS, or change operating-system configuration. It works with address values and network rules. This separation is useful for firewalls, allowlists, inventories, logs, APIs, and configuration validators.

Create an address automatically

from ipaddress import ip_address

ipv4 = ip_address("192.0.2.10")
ipv6 = ip_address("2001:db8::10")

print(ipv4.version)
print(ipv6.compressed)

ip_address() selects IPv4 or IPv6 and raises ValueError for invalid input. When the expected version is known and detailed errors matter, instantiate IPv4Address or IPv6Address directly.

Validate user input

import ipaddress

def parse_ip(text: str):
    try:
        return ipaddress.ip_address(text.strip())
    except ValueError as exc:
        raise ValueError("invalid IP address") from exc

IPv4 strings with leading zeroes, such as 010.0.0.1, are rejected to avoid ambiguity with octal notation. Do not automatically repair suspicious values because a rejected string may silently become a different address.

Address, network, and interface

  • ip_address("10.0.0.5") represents one address.
  • ip_network("10.0.0.0/24") represents a range.
  • ip_interface("10.0.0.5/24") preserves the host and exposes its network.
from ipaddress import ip_interface

interface = ip_interface("10.0.0.5/24")
print(interface.ip)
print(interface.network)
print(interface.with_netmask)

Strict network parsing

By default, ip_network() requires host bits to be clear:

from ipaddress import ip_network

network = ip_network("192.168.1.0/24")
normalized = ip_network("192.168.1.42/24", strict=False)
print(normalized)  # 192.168.1.0/24

Use strict=False only when normalization is intentional and documented. In security rules, silently masking host bits can hide a configuration error.

Test network membership

from ipaddress import ip_address, ip_network

network = ip_network("10.20.0.0/16")
client = ip_address("10.20.8.15")

if client in network:
    print("allowed")

IPv4 objects should be matched with IPv4 networks and IPv6 objects with IPv6 networks. Mixed versions normally raise TypeError, so validate the version before applying rules.

Address properties

from ipaddress import ip_address

address = ip_address("127.0.0.1")
print(address.is_loopback)
print(address.is_private)
print(address.is_global)
print(address.is_multicast)
print(address.is_link_local)
print(address.reverse_pointer)

reverse_pointer returns the PTR query name; it does not perform DNS resolution. The meanings of is_private and is_global follow IANA special registries and may be corrected across Python versions. Python 3.13 fixed multiple false positives and negatives, so avoid replacing the library with stale hard-coded lists.

Private does not mean trusted

A private, loopback, or link-local address is not proof of identity. In web applications, X-Forwarded-For can be forged unless the complete proxy chain is controlled and configured. Obtain the effective client address from a trusted source.

Likewise, is_global describes reachability classification, not reputation, ownership, authentication, or authorization.

Network details and hosts

from ipaddress import ip_network

network = ip_network("192.0.2.0/29")
print(network.network_address)
print(network.broadcast_address)
print(network.netmask)
print(network.hostmask)
print(network.num_addresses)
print(list(network.hosts()))

IPv4 hosts() usually excludes network and broadcast addresses. A /31 includes both endpoints for point-to-point links, and a /32 contains one host. IPv6 rules differ, so do not transfer IPv4 broadcast assumptions.

Do not materialize huge networks

An IPv6 /64 has an impractical number of addresses. Use membership tests, indexing, properties, and subnet operations instead of converting it to a list.

from ipaddress import ip_network

network = ip_network("2001:db8::/64")
print(network.num_addresses)
print(network[0])
print(network[-1])

Split and expand networks

from ipaddress import ip_network

network = ip_network("10.0.0.0/24")
for subnet in network.subnets(new_prefix=26):
    print(subnet)

larger = network.supernet(new_prefix=22)

A subnet prefix must be longer than the original. A supernet prefix must be shorter.

Compare and overlap networks

from ipaddress import ip_network

main = ip_network("10.0.0.0/8")
branch = ip_network("10.20.0.0/16")
segment = ip_network("10.20.128.0/17")

print(branch.subnet_of(main))
print(main.supernet_of(segment))
print(branch.overlaps(segment))

These operations help identify redundant rules, route conflicts, and overlapping allocations.

Exclude one range

from ipaddress import ip_network

block = ip_network("192.0.2.0/28")
reserved = ip_network("192.0.2.4/30")

for remaining in block.address_exclude(reserved):
    print(remaining)

The excluded network must be fully contained. The result is an iterator of CIDR networks covering the remaining addresses.

Summarize and collapse

import ipaddress

first = ipaddress.ip_address("192.0.2.0")
last = ipaddress.ip_address("192.0.2.130")
print(list(ipaddress.summarize_address_range(first, last)))

networks = [
    ipaddress.ip_network("10.0.0.0/25"),
    ipaddress.ip_network("10.0.0.128/25"),
]
print(list(ipaddress.collapse_addresses(networks)))

collapse_addresses() merges adjacent or redundant blocks. Do not mix IPv4 and IPv6 in one call.

Convert to integers and packed bytes

from ipaddress import ip_address

address = ip_address("192.0.2.1")
print(int(address))
print(address.packed)
print(str(address))

packed returns four bytes for IPv4 and sixteen for IPv6 in network byte order. Combine it with Python struct for binary headers, while explicitly documenting version and field length.

IPv6 display and scopes

from ipaddress import IPv6Address

address = IPv6Address("2001:db8::1")
print(address.compressed)
print(address.exploded)

local = IPv6Address("fe80::1234%eth0")
print(local.scope_id)

Link-local addresses may include a zone ID. Equal numeric addresses with different scope IDs are not equal. Integer conversion drops the scope, so do not use it when the interface is part of identity.

IPv4-mapped IPv6

ipv4_mapped exposes the embedded IPv4 value in addresses such as ::ffff:192.0.2.1. Recent Python versions make properties such as is_private and is_global follow that embedded address. Normalize representations so equivalent endpoints do not bypass policy through different syntax.

Build an allowlist

from ipaddress import ip_address, ip_network

ALLOWED = tuple(map(ip_network, [
    "192.0.2.0/24",
    "2001:db8:abcd::/48",
]))

def is_allowed(text: str) -> bool:
    address = ip_address(text)
    return any(
        address.version == network.version and address in network
        for network in ALLOWED
    )

Parse networks during startup rather than on every request. Python configparser can load configuration, but critical access rules still require schema validation and review.

SSRF requires multiple defenses

ipaddress helps reject loopback, private, and link-local targets, but it cannot solve SSRF by itself. A hostname may resolve to multiple addresses, change between validation and connection, or redirect elsewhere. Resolve and validate every result, connect to the validated address, restrict protocols and ports, and revalidate redirects. Parse URLs with urllib.parse rather than string concatenation.

Hashing and sorting

Address and network objects are immutable and hashable, so they can be dictionary keys and set members. Same-version addresses are orderable. When a tool intentionally mixes addresses and networks, get_mixed_type_key() provides a sorting key.

Observability

Return a simple validation message to users while logging a sanitized value, the matching rule, and IP version. For complex pipelines, use Python trace. Large validation jobs can be distributed with Python queue, while parsed networks remain shared immutable objects.

Testing strategy

Test valid IPv4 and IPv6, leading zeroes, invalid prefixes, strict host bits, /31, /32, /127, /128, mapped addresses, zone IDs, overlapping networks, enormous ranges, whitespace, confusable Unicode, and overlong strings.

Best practices

  • Parse strings once and keep objects.
  • Use strict mode for critical rules.
  • Never materialize huge networks.
  • Do not treat a private address as identity.
  • Support IPv4 and IPv6 deliberately.
  • Normalize equivalent representations.
  • Bound lists and ranges.
  • Combine IP validation with DNS, proxy, and application policy.

Conclusion

Python ipaddress provides a reliable foundation for address validation, CIDR calculations, and consistent IPv4/IPv6 policies. Immutable objects replace fragile string manipulation and make network operations explicit.

Read the official ipaddress documentation and the IANA special-purpose registry. Reachability classification is useful, but authorization always needs additional context.

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    A vibrant array of colored thread spools neatly organized in rows, perfect for sewing enthusiasts.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python queue: Coordinate Threads

    Learn Python queue to coordinate threads with FIFO, priority, backpressure, task tracking, retries, and graceful shutdown.

    Ler mais

    Tempo de leitura: 4 minutos
    17/08/2026
    Extreme close-up of computer code displaying various programming terms and elements.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python struct: Pack Binary Data

    Learn Python struct to pack binary values, define endianness, reuse buffers, parse records, and validate external protocols safely.

    Ler mais

    Tempo de leitura: 4 minutos
    17/08/2026
    Detailed shot of a Jungle Carpet Python (Morelia spilota cheynei) in its natural habitat.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python tarfile: Create Safe TARs

    Learn Python tarfile to create compressed TARs, inspect members, and extract archives with filters, limits, and path traversal protection.

    Ler mais

    Tempo de leitura: 4 minutos
    17/08/2026
    Row of colorful office binders neatly arranged on a shelf, ideal for organization concepts.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python gzip: Compress .gz Files

    Learn Python gzip to read and write .gz files, produce reproducible streams, process large data, and limit external expansion.

    Ler mais

    Tempo de leitura: 4 minutos
    17/08/2026
    Neatly arranged blue office binders labeled with dates and names for organized storage.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python lzma: Compress XZ Files

    Learn Python lzma to create XZ files, process streams, select checks and filters, and enforce memory limits on external data.

    Ler mais

    Tempo de leitura: 4 minutos
    17/08/2026
    Detailed close-up texture of a snake's patterned skin showcasing natural patterns and scales.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python bz2: Compress with bzip2

    Learn Python bz2 to compress files and bytes, process data incrementally, handle concatenated streams, and limit external expansion.

    Ler mais

    Tempo de leitura: 4 minutos
    16/08/2026