HN Debrief

IPv6 zones in URLs are a mistake

  • Infrastructure
  • Security
  • Programming
  • Developer Tools
  • Open Source

The post walks through a narrow but nasty edge case in IPv6. Link-local IPv6 addresses live in the `fe80::/10` range and are only valid on a single local network segment, so a machine with multiple interfaces may need a zone identifier like `%eth0` to say which link to use. That already makes the address host-specific. The trouble starts when you try to embed it in a URL. `%` is also the escape character for percent-encoding, so syntax like `http://[fe80::4%eth0]:80` collides with URL rules. Older guidance in RFC 6874 said to encode the percent sign as `%25`, but that guidance was later withdrawn by RFC 9844 after browsers found it impractical. The result is exactly what the post complains about. Some tools accept `%25`, some accept raw `%`, some accept both, and browsers mostly do not support the case in a usable way at all.

If your product surfaces raw link-local IPv6 URLs, expect parser bugs, browser incompatibilities, and security footguns. Prefer hostnames, mDNS, ULAs, or normal global IPv6 addresses, and if you must accept zone IDs, pin one parsing rule and test it across every runtime you ship.

Discussion mood

Mostly frustrated and amused. People agreed the syntax is ugly and the standards history is a mess, but many objected to blaming IPv6 itself because scoped link-local addresses solve a real routing ambiguity and the bigger mistake is using them in URLs for human-facing workflows.

Key insights

  1. 01

    IPv6 solved an ambiguity IPv4 mostly dodged

    Scoped link-local addresses exist because a host can have the same `fe80::` destination reachable through multiple interfaces, and the kernel cannot infer the right link from the address alone. IPv4 link-local never got a standard way to express that disambiguation, so it mostly avoided the feature or treated `169.254.x.x` as a failure mode. That makes zones less like gratuitous complexity and more like IPv6 exposing a real problem other stacks left underspecified.

    Do not assume this is an optional oddity you can ignore if your software touches raw IPv6 literals. If you support multi-homed hosts, tunnels, or provisioning flows, decide explicitly whether you support scoped addresses or reject them.

      Attribution:
    • mananaysiempre #1
    • themafia #1 #2
    • WhyNotHugo #1
  2. 02

    ULAs and names are the practical escape hatch

    For most service access, `fe80::` is the wrong tool. Commenters pointed out that Unique Local Addresses can give you stable private IPv6 endpoints that route normally, while mDNS can publish hostnames so users never see a raw address at all. That does not replace link-local for neighbor discovery and zero-infrastructure cases, but it does avoid leaking interface-local state into app-layer identifiers.

    If you control the network, assign ULAs for internal services and publish names over DNS or mDNS. Reserve link-local literals for bootstrap, diagnostics, or system plumbing, not regular product UX.

      Attribution:
    • neilalexander #1 #2
    • labcomputer #1 #2
    • cassianoleal #1
  3. 03

    The standards story is broken enough that libraries diverged

    RFC 6874 tried to standardize zone IDs in URIs by requiring `%25` before the zone string. RFC 9844 later obsoleted that guidance after browser implementers concluded it was not workable. That left library authors in a bind. Go keeps the old behavior because shipped code depends on it. One Rust URL library rejects the syntax while another accepts both raw `%` and `%25`. Some developers now choose to reject zone IDs entirely because there is no stable cross-platform contract to serialize and parse them.

    Treat zone-bearing URLs as an interoperability hazard, not a standards-backed primitive. Build conformance tests across your chosen languages and platforms, and consider refusing this syntax at API boundaries unless you truly need it.

      Attribution:
    • neild #1 #2
    • agwa #1 #2
    • johneth #1
    • OptionOfT #1
  4. 04

    Zone IDs turn weak input validation into command injection bait

    One commenter gave a concrete exploit path. Python's `ipaddress` can accept strings with zone IDs, including shell metacharacters, so code that validates an input as an IP address and then interpolates it into a shell command can become command injection. The real bug is shelling out unsafely, not Python honoring the syntax, but scoped IPv6 widens the gap between `looks like an IP` and `safe to pass downstream`. Another commenter noted that even without a shell, argv values that begin with `-` can still be reinterpreted as flags by the called program.

    Never treat `valid IP address` as equivalent to `safe process argument`. Pass commands as argv arrays, use `--` where relevant, and add test cases with scoped IPv6 strings to any validator that feeds subprocesses.

      Attribution:
    • Sohcahtoa82 #1
    • rtpg #1
    • AshamedCaptain #1
    • btown #1
    • deepsun #1
  5. 05

    Browsers rejected the case because zone IDs are not shareable

    A core reason browser support stalled is that a zone identifier is meaningful only on the local machine. It makes sense in an address bar where the browser can hand the host string to the OS, but it stops making sense once it appears in HTML links, bookmarks, copied URLs, or the HTTP `Host` header. That mismatch explains why browsers backed away even though operators still run into real cases like router admin pages on link-local addresses.

    If you are building browser-facing tooling, do not assume address-bar acceptance means the value can safely flow through the rest of the web stack. Keep any zone handling local to the client UI layer and strip or replace it before generating links.

      Attribution:
    • evgpbfhnr #1
    • Dagger2 #1 #2
    • thayne #1

Against the grain

  1. 01

    Percent-encoding is ugly but conceptually straightforward

    One pushback was that the complaint is overstated. `%` already means percent-encoding in URIs, so encoding the zone separator as `%25` is consistent with the rest of URL parsing, and it only needs one decode pass to recover the literal percent. That view does not solve ecosystem divergence, but it is a fair reminder that the syntax itself is not inherently ambiguous if everyone follows the same rule.

    If you own both producer and consumer, RFC 6874-style `%25` handling can be a workable private contract. The risk comes when that string leaves your boundary and hits browsers or libraries with different assumptions.

      Attribution:
    • singpolyma3 #1 #2
  2. 02

    Browsers could support this in the address bar

    A minority view held that scoped IPv6 is a valid local-machine feature and browsers already do plenty of input magic, so special-casing zone IDs in the address bar is not qualitatively different. The key restriction is that the zone should stay client-local and not propagate as part of the wider web model. That framing narrows the problem from impossible to awkward but implementable.

    For local admin tools or native shells around a browser engine, limited support can be reasonable if it stays in direct user input only. Do not extend that support to generated links, remote content, or anything that leaves the machine.

      Attribution:
    • KingMachiavelli #1
    • thayne #1
    • GalaxyNova #1

In plain english

argv
The array of command-line arguments passed directly to a process when it starts.
IPv6
Internet Protocol version 6, the newer internet addressing system with vastly more addresses and fewer reasons to rely on NAT.
link-local
An address that is only valid on the local network segment and is not forwarded by routers.
mDNS
Multicast Domain Name System, a way for devices on a local network to discover each other without a conventional DNS server.
percent-encoding
The URL mechanism that represents special characters using `%` followed by hexadecimal digits, such as `%20` for a space.
RFC 6874
An Internet standards document that specified how to represent IPv6 zone identifiers in URIs using `%25`, later made obsolete.
RFC 9844
An Internet standards document that replaced RFC 6874 and removed URI syntax guidance for IPv6 zone identifiers after browser implementers found it impractical.
ULA
Unique Local Address, an IPv6 address range intended for private internal networks, similar in role to private IPv4 ranges like 192.168.x.x.
URL
Uniform Resource Locator, the web address used to locate a file, page, or service online.
zone identifier
A suffix like `%eth0` on an IPv6 address that tells the operating system which local network interface to use for a scoped address.

Reference links

Standards and specs

  • WHATWG issue on IPv6 zone IDs in URLs
    Referenced as part of the rationale for browsers backing away from this syntax
  • RFC 6874
    The older RFC that defined `%25` encoding for zone identifiers in URIs
  • RFC 9844
    The newer RFC that obsoleted RFC 6874 and removed URI guidance

Browser and implementation references

Workarounds and alternate encodings

Related bug examples