Skip to main content
Blog|
Learning center

What is a URI and how is it different from a URL

|
Mar 31, 2026|9 min read
LEARNING CENTERWhat is a URI and how is itdifferent from a URLHOSTNEYhostney.comMarch 31, 2026

URI stands for Uniform Resource Identifier. It is a string that identifies a resource. A URL is a type of URI that also tells you how to access the resource. Every URL is a URI, but not every URI is a URL. That distinction sounds academic until you are reading API documentation, debugging HTTP requests, or trying to understand why a specification uses “URI” when you expected “URL.”

This guide explains what URIs are, how they differ from URLs and URNs, what the parts of a URI mean, and how they show up in web development and server administration.

URI vs URL vs URN#

These three terms describe overlapping concepts, and the confusion around them has existed since the specifications were written.

URI (Uniform Resource Identifier) is the umbrella term. A URI identifies a resource. That resource could be a web page, a file, a mailbox, a book, or an abstract concept. The identifier might tell you where the resource is, or it might just give the resource a unique name. Both of those are URIs.

URL (Uniform Resource Locator) is a URI that tells you how and where to access the resource. It includes a scheme (like https:// ) and a network location (like www.example.com ). A URL is actionable. You can put it in a browser and get something back.

https://www.example.com/blog/article?id=123

This is both a URI and a URL. It identifies the resource (the article with id 123) and tells you how to access it (HTTPS protocol, on the server www.example.com, at the path /blog/article).

URN (Uniform Resource Name) is a URI that gives a resource a persistent, location-independent name. It identifies the resource but does not tell you where to find it or how to access it.

urn:isbn:978-0-13-468599-1

This is a URI and a URN. It identifies a specific book by its ISBN. But you cannot put it in a browser and get the book. You need some other system (a library catalog, Amazon, a bookstore) to locate it. The URN is just the name.

The practical takeaway

In day-to-day web development and server administration, the distinction between URI and URL rarely matters. When you see “URI” in documentation, you can mentally substitute “URL” in most cases and be correct. The RFC specifications use “URI” as the technically precise term, but virtually every URI you encounter in web work is a URL.

The main place the distinction matters is in API specifications and HTTP standards. The HTTP specification refers to the target of a request as a “request URI” (technically correct) rather than a “request URL” (common usage). If you are reading RFC 7230 and see “request-target,” it means the URI in the HTTP request line.

Anatomy of a URI#

A URI has up to five components, defined by RFC 3986:

scheme://authority/path?query#fragment

Using a concrete example:

https://www.example.com:443/blog/article?id=123&lang=en#comments

Scheme

https

The scheme identifies the protocol or the naming system. For web URLs, this is typically http or https . Other common schemes include ftp , ssh , mailto , tel , and file .

The scheme is followed by :// for URLs that include an authority component. The mailto scheme is an exception. mailto:user@example.com uses a colon without the double slash because it has no authority component.

Authority

www.example.com:443

The authority identifies the server (or naming authority) responsible for the resource. For web URLs, this is the domain name and optionally a port number. The full syntax is:

[userinfo@]host[:port]
  • userinfo is rarely used in modern web URLs but exists in the spec.  user:password@example.com  is valid syntax, though browsers have largely deprecated showing credentials in the URL bar for security reasons.
  • host is the domain name or IP address.
  • port is the network port. If omitted, the default port for the scheme is used (80 for HTTP, 443 for HTTPS, 21 for FTP, 22 for SSH).

In practice, you almost never include the port in a URL because HTTPS on port 443 is the default. You only specify it when using a non-standard port, like http://localhost:3000 during development.

Path

/blog/article

The path identifies the specific resource on the server. It is hierarchical, using forward slashes to separate segments. The path can represent a file on disk ( /images/logo.png ), a route handled by an application ( /blog/article ), or an API endpoint ( /wp-json/wp/v2/posts ).

An empty path or a path of just / refers to the root resource, which is typically the homepage of a website or the base endpoint of an API.

Query

?id=123&lang=en

The query string contains key-value pairs that provide additional parameters to the resource. It starts with ? and pairs are separated by & . The query does not identify a different resource. It modifies the request to the same resource. /blog/article?id=123 and /blog/article?id=456 point to the same endpoint but request different data.

Query strings are used extensively in:

  • Search pages ( ?q=search+term )
  • Pagination ( ?page=2 )
  • Filtering and sorting ( ?category=hosting&sort=date )
  • Tracking parameters ( ?utm_source=newsletter )
  • API requests ( ?per_page=10&orderby=date )

Fragment

#comments

The fragment identifier points to a specific section within the resource. It starts with # . Importantly, the fragment is never sent to the server. It is handled entirely by the client (browser). When you click a link to https://example.com/article#comments , the browser requests https://example.com/article from the server and then scrolls to the element with id="comments" on the page.

Because fragments are client-side only, they do not appear in server access logs. If you are debugging a request and wondering why the fragment is missing from your Nginx logs, that is expected behavior.

Single-page applications (SPAs) use fragments for client-side routing: https://app.example.com/#/dashboard , https://app.example.com/#/settings . The server always serves the same page, and the JavaScript application reads the fragment to determine which view to render. This is the “hash routing” pattern.

Absolute vs relative URIs#

Absolute URI

An absolute URI includes the scheme and authority, providing everything needed to locate the resource:

https://www.example.com/blog/article

You can give this to anyone and they can access the resource regardless of context.

Relative URI

A relative URI omits the scheme and authority and is resolved relative to a base URI (usually the current page):

/blog/article
../images/logo.png
article?id=123

Relative URIs are common in HTML. When you write <a href="/about">About</a> in your HTML, the browser resolves it against the current page’s URL. If you are on https://example.com/blog/ , the link resolves to https://example.com/about .

Protocol-relative URIs omit only the scheme:

//cdn.example.com/style.css

The browser uses the same protocol as the current page (HTTPS if the page is HTTPS, HTTP if HTTP). This pattern was common when sites served both HTTP and HTTPS, but now that HTTPS is standard, it is better to use explicit https:// everywhere. Protocol-relative URIs are still valid but considered a legacy pattern.

Why this matters for WordPress

WordPress stores full absolute URLs in the database for post content, images, and links. When you migrate a WordPress site to a new domain, every URL in the database needs to be updated. This is why WordPress migrations require a search-and-replace operation on the database, and why simply copying files is not enough. Relative URLs would avoid this problem, but WordPress made the design decision to use absolute URLs, and that decision is deeply embedded in the codebase.

URIs in HTTP requests#

When your browser sends an HTTP request, the request line contains the URI:

GET /blog/article?id=123 HTTP/1.1
Host: www.example.com

The request URI here is /blog/article?id=123 , which is the path and query without the scheme or authority. The authority is in the Host header instead. This is how HTTP/1.1 works: the URI in the request line is relative, and the Host header provides the domain.

In HTTP/2 and HTTP/3, the components are split across pseudo-headers ( :method , :path , :authority , :scheme ), but the concept is the same.

When you see a 405 Method Not Allowed error, the server is saying it understood the URI but does not support the HTTP method you used on it. When you see a 404 Not Found, the server could not find a resource matching the URI path.

URIs in REST APIs#

REST APIs use URIs as resource identifiers. The WordPress REST API is a practical example:

GET /wp-json/wp/v2/posts           # List all posts
GET /wp-json/wp/v2/posts/123       # Get post with ID 123
POST /wp-json/wp/v2/posts          # Create a new post
PUT /wp-json/wp/v2/posts/123       # Update post 123
DELETE /wp-json/wp/v2/posts/123    # Delete post 123

The URI /wp-json/wp/v2/posts/123 identifies a specific resource (post 123). The HTTP method (GET, POST, PUT, DELETE) determines what operation is performed on that resource. The same URI with different methods produces different results.

This is the core principle of REST: URIs identify resources, HTTP methods define actions. A well-designed REST API has predictable URI patterns:

/resource                # Collection
/resource/{id}           # Specific item
/resource/{id}/related   # Related sub-resource

Query parameters filter or modify the collection:

/wp-json/wp/v2/posts?per_page=5&orderby=date&order=desc

URI encoding#

URIs can only contain a specific set of characters. Letters, digits, hyphens, periods, underscores, and tildes are safe. Everything else must be percent-encoded: the character’s byte value expressed as %XX in hexadecimal.

Common encodings:

CharacterEncodedWhere you see it
Space %20  or  + Search queries, file names
/ %2F When a literal slash is needed in a path segment
? %3F When a literal question mark is needed outside the query
& %26 When a literal ampersand is needed in a query value
# %23 When a literal hash is needed (not a fragment)
@ %40 Email addresses in URIs

If you see %20 in a URL, it is a space. https://example.com/my%20file.pdf points to a file named “my file.pdf”. Browsers display the decoded version in the address bar but send the encoded version in the HTTP request.

When building URLs programmatically, use your language’s built-in encoding functions rather than manually replacing characters. In PHP: urlencode() for query values, rawurlencode() for path segments. In JavaScript: encodeURIComponent() for individual values, encodeURI() for complete URIs.

Common URI mistakes#

Confusing path and query in Nginx. Nginx’s $uri variable contains the path without the query string. $request_uri contains both. If you use $uri in a redirect and the original request had query parameters, they are lost. Use $request_uri when you need to preserve the full URI:

# Preserves query string
return 301 https://example.com$request_uri;

# Loses query string
return 301 https://example.com$uri;

Forgetting to encode special characters. A URL containing unencoded spaces, brackets, or Unicode characters may work in some browsers but fail in API calls, curl, and automated tools. Always encode.

Using fragments for server-side routing. Fragments are never sent to the server. If your application depends on the server seeing #section in the URL, it will not work. Use query parameters or path segments instead.

Quick reference#

TermDefinitionExample
URIIdentifies a resource (umbrella term) https://example.com/page
URLURI that locates a resource (includes scheme + authority) https://example.com/page
URNURI that names a resource (location-independent) urn:isbn:978-0-13-468599-1
SchemeProtocol or naming system https ftp mailto
AuthorityServer identifier (host + optional port) www.example.com:443
PathResource location on the server /blog/article
QueryKey-value parameters ?id=123&lang=en
FragmentClient-side section identifier (not sent to server) #comments