Development & Code

URI vs URL: What the Specs Actually Say

People use URI and URL as if they are the same word. Most of the time nobody is harmed by it, but the two terms are not interchangeable in both directions, and the reason why is more interesting than the usual one-line answer.

The short version you have probably heard is right: a URI identifies a resource, a URL is the kind of URI that also tells you how to go get it. All URLs are URIs. Not all URIs are URLs.

That is true. It is also about half of the story, and the missing half is the part that explains why you see the terms used inconsistently in real specs and real code.

What the spec actually says

The authority here is RFC 3986, “Uniform Resource Identifier (URI): Generic Syntax”, published January 2005 and adopted as Internet Standard STD 66. Section 1.1.3 is titled “URI, URL, and URN” and defines the relationship in one sentence:

The term “Uniform Resource Locator” (URL) refers to the subset of URIs that, in addition to identifying a resource, provide a means of locating the resource by describing its primary access mechanism (e.g., its network “location”).

So a URL is not a different thing from a URI. It is a URI that carries extra information: how to reach the thing. https://thecaptaindumbass.com/ names a resource and also tells you the protocol to speak and the host to speak it to. That makes it a locator, so it is a URL.

A URI has no such obligation. RFC 3986 is blunt about how little it assumes, saying the term resource covers “whatever might be identified by a URI” and that a resource “is not necessarily accessible via the Internet”. Human beings and bound books in a library are given as examples of resources.

The identifiers that are not locators

This is where the one-way rule earns its keep. Some URIs identify something without telling you where to get it or how.

URIIdentifiesTells you how to fetch it
urn:isbn:0451450523A specific book editionNo
urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6A unique valueNo
urn:oasis:names:specification:docbook:dtd:xml:4.1.2A named specificationNo
https://example.com/report.pdfA documentYes, HTTPS to that host
ftp://ftp.example.com/rfc/rfc1808.txtA fileYes, FTP to that host
file:///etc/hostsA local fileYes, the local filesystem

The urn: examples are identifiers and nothing more. Handing an ISBN URN to a browser accomplishes nothing, because it was never meant to describe an access mechanism. That is exactly what “not all URIs are URLs” means.

Worth noting that URN syntax has moved on since RFC 3986 was written. It now lives in RFC 8141 from 2017, which replaced the older RFC 2141 that RFC 3986 cites.

The trap: name or locator is not a property of the scheme

Here is the first place people get too confident. It is tempting to sort schemes into two bins, locators over here and names over there. RFC 3986 says that is not how it works:

An individual scheme does not have to be classified as being just one of “name” or “locator”. Instances of URIs from any given scheme may have the characteristics of names or locators or both, often depending on the persistence and care in the assignment of identifiers by the naming authority, rather than on any quality of the scheme.

That is why arguing about whether mailto: or tel: is “really a URL” tends to go nowhere. The classification depends on how an identifier is used and assigned, not on the four letters before the colon.

You can see the same effect with HTTP identifiers that were never meant to be fetched. XML namespace names and many RDF and JSON-LD vocabulary terms are http URIs used purely as globally unique names. They are syntactically URLs. Typing one into a browser may well 404, and that breaks nothing, because identifying a resource has never implied that anything will retrieve it. RFC 3986 makes that explicit too, noting that “in many cases, URIs are used to denote resources without any intention that they be accessed”.

The plot twist: the spec tells you to stop saying URL

If you only take one new thing from this post, take this. Right after defining URL and URN, RFC 3986 gives an instruction that almost nobody quotes:

Future specifications and related documentation should use the general term “URI” rather than the more restrictive terms “URL” and “URN”.

The IETF position, as of 2005, is that the URL and URN labels cause more confusion than they resolve, and that you should just say URI. That recommendation traces back to RFC 3305, a joint W3C and IETF report on exactly this terminology mess.

So the pedant who corrects you with “well actually, that is a URI” is standing on solid ground.

And then the web went the other way

Except the web platform looked at that advice and did the opposite. The WHATWG URL Standard, which is what browsers actually implement, lists this among its goals:

Standardize on the term URL. URI and IRI are just confusing. In practice a single algorithm is used for both so keeping them distinct is not helping anyone.

Its other stated goal is to align RFC 3986 and RFC 3987 with what implementations really do “and obsolete the RFCs in the process”.

Read those two documents together and the situation is clear. The IETF says prefer URI. The browser world says prefer URL and treats it as the broad term for anything its parser accepts. Both are current. Both are legitimate in their own domain. That is the actual reason the words get mixed up everywhere, and it is not because everyone is sloppy.

Practical consequence: when you are reading a spec, check whose spec it is before you assume which definition of URL is in play.

The distinction that matters more day to day

While everyone argues about URI and URL, the genuinely useful distinction gets ignored. A bare /posts/some-article/ or #section-3 is neither a URI nor a URL. It has no scheme. RFC 3986 section 4.1 handles it with a separate term:

URI-reference = URI / relative-ref

A URI-reference is either a full URI or a relative reference, and the spec says that if the prefix “does not match the syntax of a scheme followed by its colon separator, then the URI-reference is a relative reference”.

That is the category almost everything in your HTML lives in. An href of /about/ is a relative reference that gets resolved against a base. When someone says “put the URL in the config”, the string they hand you may be a full URI, a relative reference or a bare hostname, and those need different handling. Knowing that distinction prevents bugs. Knowing whether to say URI or URL prevents nothing.

Where it actually bites in code

Java is the clearest cautionary tale, because it shipped both concepts as separate classes and the difference is not cosmetic. java.net.URI does parsing and identity. java.net.URL is tied to actually opening connections, and the javadoc for its equals method spells out the consequence:

Two hosts are considered equivalent if both host names can be resolved into the same IP addresses […] Since hosts comparison requires name resolution, this operation is a blocking operation.

Comparing two URL objects can perform a DNS lookup. Put URL objects in a HashMap or a HashSet and your hash operations may hit the network, which is a fantastic way to build something that works on your machine and stalls in production. The javadoc adds that the behavior “is known to be inconsistent with virtual hosting in HTTP”. Use URI for anything involving identity, comparison or keys, then convert with toURL() only when you are about to open a connection.

Most other stacks skipped the split. .NET has a single System.Uri covering both jobs. Python’s urllib.parse speaks of URLs while following the RFC syntax. JavaScript has URL, which follows the WHATWG rules and, notably, refuses to construct from a relative reference unless you supply a base.

How I use the terms

  • URI when I mean an identifier and I do not care whether it can be fetched.
  • URL when the thing genuinely locates a resource, and when talking to browser people or reading web platform specs.
  • Relative reference when there is no scheme, because that is the distinction that changes what my code has to do.
  • I do not correct people in conversation unless the ambiguity is about to cause a real problem, because both standards bodies disagree with each other and neither of them is going to back down.

Your original instinct was correct. All URLs are URIs and not all URIs are URLs. Just know that the spec which defines that relationship also asks you to stop leaning on the word URL, while the spec your browser runs on asks for the exact opposite.

Sources

Your turn

Do you say URI or URL at work, and does anyone notice? Have you been bitten by the Java URL.equals DNS lookup, or by a config field that accepted a relative reference when the code expected a full one? The comments are open.

// comments

← all posts more in Development & Code →