Everyone learns the private ranges early: 10.x, 172.16 through 172.31.x, and 192.168.x. Two other blocks turn up just as often in real troubleshooting and get explained half as carefully. One is 127.0.0.0/8, which a machine uses to talk to itself. The other is 169.254.0.0/16, which is what a machine assigns itself when everything else has already failed.
They look like ordinary addresses. They behave nothing like them, and the whole difference is scope: how far a packet carrying one is allowed to travel before something drops it.
Two ways of going nowhere#
A block of addresses is really defined by what routers are willing to do with it, and IANA writes that decision down. In the IPv4 special-purpose address registry, 127.0.0.0/8 is listed as Loopback, citing RFC 1122 section 3.2.1.3, and 169.254.0.0/16 is listed as Link Local, citing RFC 3927. Both carry the same two flags: Forwardable is False, Globally Reachable is False.
The flags match. The reason they match does not. A loopback packet never touches a wire at all, because the network stack recognises the destination and turns the packet around inside the machine. A link-local packet does go out on the wire, reaches every device on that segment, and then stops dead at the first router that sees it.
IPv6 has both, with identical flags in its own registry: ::1/128 for loopback and fe80::/10 for link-local, both defined by RFC 4291. Everything else in this post follows from those four rows.
The loopback block is bigger than one address#
Almost everyone knows 127.0.0.1. Fewer people notice that RFC 1122 reserved the entire 127.0.0.0/8, which is 16,777,216 addresses, for a job that looks like it needs exactly one.
The extra room is genuinely useful. On Linux the whole block loops back by default, so you can bind different local services to different loopback addresses and let every one of them use the same port number. Two development servers that both insist on port 8080 stop fighting the moment one binds to 127.0.0.1 and the other to 127.0.0.2. Other operating systems are stricter and want the extra address configured on the loopback interface first, so test it before you build on it.
The block has a second life as a signalling channel. When a DNS blocklist reports that an address is listed, it answers with an A record inside 127.0.0.0/8, and the final octet encodes the reason for the listing. Nothing ever connects to that address; the address is the answer. How to check if an IP is on a blacklist covers how to read those codes.
127.0.0.1 and 0.0.0.0 are not the same thing#
These two get confused constantly, and the confusion has security consequences.
0.0.0.0/8 is registered as "This network", citing RFC 791. As a destination address it is not a real place. As a bind target, operating systems read 0.0.0.0 as something else entirely: listen on every interface this machine has.
So a server bound to 127.0.0.1 can only be reached from the machine it runs on. The same server bound to 0.0.0.0 can be reached from anything that can route to any of its addresses, which on an office network means everybody. A great many accidental exposures are exactly that one-line difference in a config file, found later by somebody scanning the range.
Worth keeping in proportion: binding to 0.0.0.0 does not by itself put a service on the public internet, because NAT and firewalls usually sit in the way. NAT explained covers why that is, and why it is not something to rely on.
localhost is a name, not an address#
localhost is a hostname, and it gets resolved before anything reaches the network at all. RFC 6761 makes it a special-use name with rules attached: resolver libraries "SHOULD always return the IP loopback address for address queries", and caching DNS servers should generate an immediate answer rather than asking anyone upstream.
The trap is dual stack. On a machine with IPv6 enabled, localhost normally resolves to both ::1 and 127.0.0.1, and the client library picks one. If your service is listening only on 127.0.0.1 and the client tried ::1 first, you get "connection refused" from a server that is unmistakably running. Connect to 127.0.0.1 by address rather than by name, and if that works you have found your bug.
169.254 means DHCP gave up#
An interface showing a 169.254.x.x address is not configured. It is reporting a failure.
RFC 3927 describes what happens when a host cannot get a DHCP lease: it picks an address out of 169.254.0.0/16 for itself. Microsoft's name for this is APIPA. The RFC reserves the first 256 and last 256 addresses of the block, so a host choosing one takes it from 169.254.1.0 to 169.254.254.255 inclusive. Before using it, the host has to test whether anyone else already holds it, which it does by sending ARP probes, then announces its claim.
The result is a segment where machines can still reach each other and nothing can reach a gateway. Section 7 of the RFC is blunt about the second half: "A router MUST NOT forward a packet with an IPv4 Link-Local source or destination address, irrespective of the router's default route configuration or routes obtained from dynamic routing protocols."
So read 169.254.x.x as a diagnosis rather than a setting. Something between the machine and the DHCP server is broken: the server is down, the pool is exhausted, the switch port is on the wrong VLAN, or the Wi-Fi associated without ever being authorised. Assigning an address by hand clears the symptom and leaves the cause exactly where it was.
One address in the range is a deliberate exception. AWS documents the EC2 instance metadata service at http://169.254.169.254/latest/meta-data/, noting that these "are link-local addresses and are valid only from the instance". It works because the traffic stays on the local segment: every instance in the fleet can use the identical address and still reach only its own metadata.
Link-local in IPv6 is not a failure#
This is where the symmetry breaks, and it catches people out. In IPv4, a link-local address means configuration failed. In IPv6 it is required infrastructure, present on every interface whether or not anything else worked.
IPv6 depends on it. Neighbor Discovery, which replaces ARP, runs over link-local addresses, and so do the router advertisements that hand out a global address in the first place. RFC 4291 puts link-local addresses to work "for purposes such as automatic address configuration, neighbor discovery, or when no routers are present".
The format in RFC 4291 section 2.5.6 is a 10-bit prefix of binary 1111111010, then 54 zero bits, then a 64-bit interface identifier. That 10-bit prefix is why the block is written fe80::/10 and why it really spans fe80 through febf. The 54 zero bits are why every address you will actually meet starts fe80::, and that combination is a trap for anyone writing a check: "does it start with fe80" looks correct and quietly misses three quarters of the range it claims to cover. Our own address validator matches the full /10 for exactly that reason.
An interface holding a fe80:: address and no global one is the IPv6 counterpart of 169.254: router advertisements are not arriving. What is my IPv6 address covers what a working setup looks like.
Why fe80::1 alone is not enough#
A link-local address is only unique on its own link. A laptop with Wi-Fi and Ethernet can genuinely have fe80::1 reachable on both, with a different machine answering on each, so the address by itself identifies nothing.
The fix is a zone identifier: the address, a percent sign, then the interface. That gives fe80::1%en0 on macOS, fe80::1%eth0 on Linux, and fe80::1%12 on Windows, which numbers its interfaces rather than naming them.
Paste that straight into a browser and it fails, because the percent sign already means something in a URL. RFC 6874 sets the rule: "any occurrences of literal '%' symbols in a URI MUST be percent-encoded and represented in the form '%25'. Thus, the scoped address fe80::a%en1 would appear in a URI as http://[fe80::a%25en1]." Square brackets around the address, and %25 everywhere you typed %.
What these addresses do to tools#
Any tool that looks an address up on your behalf has to handle these ranges deliberately, because there is nothing to look up. No geolocation for 127.0.0.1, no owner for 169.254.5.5, no ASN for either. Here is how ours treat them.
- The IP address lookup carries written explanations for the well-known special-use addresses instead of an empty report. Look up 127.0.0.1 and you get the loopback block described, not a blank map.
- The CIDR calculator labels the range type on IPv6 blocks. Give it
fe80::/10and it says Link-Local; give it::1/128and it says Loopback. - The log IP triage tool keeps loopback and link-local addresses out of the ranking but still counts them, because "38 of your 40 addresses were internal" is a real answer to hand someone.
- Tools that fetch a URL you supply refuse these ranges outright. The HTTP headers, SSL inspector, response time and CSP analyzer checks all run the same guard, which rejects
127.0.0.0/8and169.254.0.0/16alongside the private ranges. That is not fussiness. As the metadata endpoint above shows,169.254.169.254is precisely the address a server-side request forgery attack aims at.
A short triage list#
- An interface shows
169.254.x.x. DHCP failed. Fix the lease, not the address. - An IPv6 interface shows only
fe80::. No router advertisements are reaching it. - A service works locally but not from another machine. It is bound to
127.0.0.1. Bind to0.0.0.0, then think carefully about the firewall. localhostrefuses but127.0.0.1connects. The client went to::1, and nothing is listening there.- A browser rejects a link-local address. Add the zone id, wrap the address in square brackets, and write the separator as
%25. - A blocklist answered with something in
127.0.0.0/8. That is a return code, not a host to connect to.
None of this is exotic. These are the two places the internet's addressing scheme deliberately stops short, and working out which one you are staring at usually explains the fault before you run anything else. For the territory either side of them, the CIDR notation explainer covers how blocks are written and the RFC 1918 ranges cover the addresses that do get routed, just not by anyone else.
See what a block actually covers
Paste any IPv4 or IPv6 block into the CIDR calculator. IPv4 gives you the network and broadcast addresses, the usable host range and the total count. IPv6 shows the last address in the block instead of a broadcast, and adds a range type: loopback, link-local, unique local, documentation, or global unicast.
Open the CIDR calculator →