Testing & QA · 6 min read

IP Address Generator in 2026: Public, Private, Loopback

IP address generator for synthetic IPv4 and IPv6 fixtures: public, private, loopback, link-local, ULA, and global ranges, plus validation rules.

By Charlie Simmon ·

An IP address generator for testing has one job: return addresses that are valid for the scope the fixture claims, without accidentally pointing at a machine that matters. The second half is where most generators fail. An address that is syntactically public can still belong to a bank, a cloud provider, or your own office uplink.

This guide covers the scopes the generator supports, the special-purpose ranges that every validator should know, the difference between a syntactically public address and a routable one, and how to build fixture sets that cannot collide with a real network. It also covers IPv6, where the scope rules are different enough that copying an IPv4 mental model produces broken tests.

What the generator returns

For IPv4 the generator produces values in four scopes: public, private, loopback, and link-local. For IPv6 it produces four scopes as well: global, unique local (ULA), link-local, and loopback. The scope is part of the request, not something you guess from the output, because the same dotted quad can be private in one context and a valid host on a private network in another.

Scope is a fixture label, not a reachability claim. A generated public-scope address is outside the private and special-purpose blocks, but it may still coincide with a real allocation somewhere on the internet. Do not send traffic to generated addresses. If a test must be certain that nothing real is contacted, use the documentation ranges described below instead of public-scope output.

  • IPv4 scopes: public, private, loopback, link-local
  • IPv6 scopes: global, unique local, link-local, loopback
  • No scope implies that traffic is safe to send to the address
  • For hard isolation, use the documentation ranges, not public-scope output

IPv4 special-purpose ranges

The blocks below are the ones a validator or a fixture builder must handle explicitly. Private space is 10.0.0.0/8, 172.16.0.0/12, and 192.168.0.0/16, which gives roughly 17.8 million addresses. Loopback is the whole 127.0.0.0/8 block, not just 127.0.0.1, and link-local is 169.254.0.0/16, familiar as the APIPA range when DHCP fails.

Beyond those, several blocks matter for tests. 100.64.0.0/10 is carrier-grade NAT space and behaves like private space on many networks. 192.0.2.0/24, 198.51.100.0/24, and 203.0.113.0/24 are reserved for documentation, which makes them ideal for fixtures that must never reach a real host. 198.18.0.0/15 is reserved for benchmarking. 0.0.0.0/8 means "this host", 224.0.0.0/4 is multicast, and 240.0.0.0/4 is reserved.

ScopeCIDRAddress countTypical use
Private10.0.0.0/816,777,216Large private networks
Private172.16.0.0/121,048,576Docker and mid-size networks
Private192.168.0.0/1665,536Home and small office LANs
Loopback127.0.0.0/816,777,216Local host only
Link-local169.254.0.0/1665,536APIPA when DHCP fails
Carrier NAT100.64.0.0/104,194,304ISP shared address space
Documentation192.0.2.0/24256Safe fixture range (TEST-NET-1)
Benchmark198.18.0.0/15131,072Network test equipment
Multicast224.0.0.0/4268,435,456Group delivery

Private space and the 10/8 question

The three private blocks are defined by RFC 1918 and are not routable on the public internet. Which one you use is a matter of scale and convention: 10/8 gives a single flat space large enough for anything, 172.16/12 is common in container networks, and 192.168/16 is the default on consumer routers. The blocks do not overlap, so a test that assumes every private address starts with 10 is wrong about two thirds of the space.

A subtlety that breaks tests: private addresses are ordinary addresses inside the private network. If your CI runner sits on 10.0.0.0/8, a generated private address in that block can point at a real internal host, including a metadata service or an admin panel. Generate from the 192.168.0.0/16 range or, better, from the documentation ranges when the test must be isolated.

  • RFC 1918 blocks: 10/8, 172.16/12, 192.168/16
  • Pick a range that does not overlap the network running the test
  • Cloud metadata endpoints live at well-known private addresses
  • An internal host is just as real as an external one

Loopback, link-local, and where they belong

Loopback addresses never leave the host. 127.0.0.1 is the common one, but the entire 127.0.0.0/8 block is loopback on most stacks, and 127.0.0.53 is used by systemd-resolved for local DNS. A test that hard-codes 127.0.0.1 as a server address will collide with anything else on the machine using the same port, which is why parallel test runs should allocate ports dynamically.

Link-local IPv4 addresses in 169.254.0.0/16 are assigned automatically when no DHCP server answers. Seeing one in production logs almost always means DHCP failed, not that a customer is on a strange network. On IPv6 the equivalent is fe80::/10, and it behaves differently: every IPv6 interface has a link-local address, it is required for neighbour discovery, and it cannot be used without a zone index because the same address exists on every link.

AddressScopeCan leave the host?Common mistake
127.0.0.1IPv4 loopbackNoAssuming only this one is loopback
127.0.0.53IPv4 loopbackNoColliding with the local resolver
169.254.1.1IPv4 link-localSame link onlyTreating it as a public address
::1IPv6 loopbackNoWriting ::1/128 as a host range
fe80::1IPv6 link-localSame link onlyOmitting the zone index

IPv6 scopes without an IPv4 mental model

IPv6 has no broadcast and no NAT requirement, and its scope rules are carried in the prefix. Global unicast currently lives in 2000::/3, which is the entire range from 2000:: to 3fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff. Unique local addresses live in fc00::/7 and are conventionally generated from fd00::/8. Link-local is fe80::/10, and ::1/128 is loopback with ::/128 as the unspecified address.

The generator returns addresses in those four scopes. Two details matter for tests: a ULA address is not globally routable and must be treated as private, and a link-local address is only meaningful with a zone identifier, for example fe80::1%eth0. If your code stores an IPv6 address, store the zone separately rather than glued into the text, because a percent sign in a URL has a different meaning.

ScopePrefixRoutableExample
Global unicast2000::/3Yes, in principle2001:db8::1
Unique localfc00::/7No, privatefd12:3456::1
Link-localfe80::/10Same link onlyfe80::1
Loopback::1/128No::1
Documentation2001:db8::/32Reserved for docs2001:db8::1

Transition addresses and other traps

IPv4-mapped IPv6 addresses take the form ::ffff:192.0.2.1 and appear when a dual-stack socket reports an IPv4 peer as IPv6. They are not general-purpose IPv6 addresses, and treating them as such produces bugs in rate limiters and access rules that compare prefixes. 6to4 addresses in 2002::/16 and Teredo addresses in 2001::/32 are transition mechanisms with their own structure; do not generate them as if they were ordinary global addresses.

Other traps are older but still present. A leading zero in an IPv4 octet, such as 010.1.1.1, is interpreted as octal by some parsers and rejected by others, so never accept it in a fixture. The abbreviated form 127.1 is accepted by some stacks as 127.0.0.1. Ports are a separate field and must never be concatenated into the address value.

  • ::ffff:0:0/96 is IPv4-mapped, not a normal IPv6 range
  • 2001:db8::/32 is documentation space and is the safe default for fixtures
  • Reject octets with leading zeros instead of guessing the base
  • Reject forms like 127.1 that rely on stack-specific expansion
  • Keep the port out of the address field; validate it as a separate integer

Building a fixture set that cannot collide

For unit tests that never open a socket, use documentation ranges: 192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24 for IPv4 and 2001:db8::/32 for IPv6. These are reserved precisely so that examples and tests cannot reach a real host. A parser should accept them as syntactically valid public-scope addresses, which makes them a good test of classification logic too.

For integration tests that do open sockets, bind to loopback and allocate the port from the operating system rather than hard-coding 8080. For tests that need a plausible remote peer, use the documentation ranges and make any client refuse to dial them. That combination gives you realistic values and a hard guarantee that a misconfiguration cannot send traffic somewhere unexpected.

NeedUseWhy
Parsing and formatting192.0.2.0/24, 2001:db8::/32Reserved, cannot reach a host
Private-scope logic192.168.0.0/16Unlikely to overlap CI networks
Loopback server127.0.0.1 with an OS-assigned portNo fixed port collisions
Link-local logic169.254.0.0/16 and fe80::/10Triggers the right branches
Scope classificationAll four scopes in one tableCatches a wrong mask or prefix

Validate output before it reaches a test

Every generated address should pass the same validator the application uses. That is the point of generating it. Assert that the family matches the requested scope, that the address is inside the expected block, and that the text form parses back to the same bits. A round trip catches zero compression errors in IPv6, where 2001:db8:0:0:0:0:0:1 must normalize to 2001:db8::1 and nothing else.

Also assert what the address is not. A private-scope fixture must not fall inside a public block, a loopback fixture must be in 127.0.0.0/8 or exactly ::1, and no fixture should ever be multicast, broadcast, or the unspecified address unless the test is explicitly about those cases. Those negative assertions are cheap and catch the most embarrassing generator bugs.

  • Round-trip: parse, format, parse, compare bits not strings
  • Family check: the value parses as the requested IP version
  • Scope check: containment in the block, not a prefix string compare
  • Negative check: not multicast, not broadcast, not unspecified
  • Normalization: IPv6 output uses the canonical compressed form

Questions

What scopes can the generator produce?+
IPv4 public, private, loopback, and link-local; IPv6 global, unique local, link-local, and loopback. The scope is chosen explicitly rather than inferred from the output.
Is a generated public IP address safe to contact?+
No. Public scope means outside the private and special blocks, not unallocated. Use the documentation ranges 192.0.2.0/24 and 2001:db8::/32 when a test must not reach a real host.
Which IPv4 block is carrier-grade NAT?+
100.64.0.0/10. It is not RFC 1918 private space, but it is not globally routable either, so treat it as private in classification logic.
Why does my IPv6 link-local address need a zone index?+
The fe80::/10 range exists separately on every link, so the same address can refer to different hosts. The zone identifier, such as a percent sign followed by an interface name, says which link you mean.
Can the generator create IPv4-mapped IPv6 addresses?+
It generates the four documented IPv6 scopes. IPv4-mapped forms like ::ffff:192.0.2.1 are a transition representation and should be handled by your parser rather than used as ordinary fixtures.

Examples are synthetic. Do not use a generated address for identity checks, account registration, or delivery.

Reference standards

Read next

All guides