TidyURLs
Go to Tools
BLOG
The TidyURLsJournal

Practical guides, tutorials, and insights about URLs, SEO, web development, and online marketing. Learn, troubleshoot, and get the most of the web.

Back to the Journal
TidyURLs Journal

A URL Comparison Workflow for Website QA

Practical insights, clear explanations, and useful ideas for understanding the web.

October 28, 20267 min read5 topics
A URL Comparison Workflow for Website QA
Explore the details
#Website QA#URL testing#Quality assurance#Web development#Release checklist
October 28, 20267 min read

Before a website release, a small URL change can send visitors to the wrong page, drop a campaign parameter, or alter how a page loads. To compare URLs for website QA, review their components—not just whether the full strings look different. This workflow helps QA testers and developers find meaningful changes, distinguish likely formatting differences, and check a set of links consistently.

Key Takeaways

  • Compare the scheme, host, path, query, and fragment separately; each can affect behavior differently.
  • Treat differences as candidates for investigation, not automatic failures. Host casing and a default port are usually formatting differences, while paths and query parameters often matter.
  • Preserve query parameter order and duplicates during review: applications may depend on them.
  • Record the source URL, expected URL, test case, and disposition for every comparison.

Which URL components should you review?

A URL such as https://shop.example.com:443/products/item?color=blue#details contains several parts that can change independently:

ComponentExampleQA question
SchemehttpsDid the link change from HTTPS to HTTP, or to a different scheme?
Hostshop.example.comDoes it point to the expected domain or subdomain?
Port:443Is a non-default or unexpected port present?
Path/products/itemDoes it resolve to the intended page? Check spelling, casing, encoding, and trailing slash.
Query?color=blueAre required parameters present, correctly valued, and appropriately ordered?
Fragment#detailsDoes the page or client-side app use this section or route?

A fragment is handled by the browser and is not sent as part of the HTTP request to the server. It can still matter to a user: it may select a page section or, in some client-side apps, identify a route. Query strings are sent with the request, but their interpretation is application-specific.

Paths also need context. URL hosts are case-insensitive, but path case can matter to the server. A trailing slash may lead to a redirect, a different resource, or no difference, depending on the site. Do not assume that two different-looking paths are interchangeable.

Meaningful changes versus formatting differences

Some changes usually warrant a closer look:

  • Scheme: http versus https changes the connection scheme and may affect redirects, security, and site behavior.
  • Host: www.example.com and example.com are different hosts, even if the site redirects one to the other.
  • Path: /guide and /guides may be separate routes. /Guide may also differ from /guide on a case-sensitive setup.
  • Trailing slash: /docs and /docs/ can behave differently. Check the site's actual routing or redirect behavior.
  • Query parameter: ?page=2 versus ?page=3 is a value change. A missing parameter can also change the result.
  • Fragment: #setup versus #install may send users to different content or app routes.

Other differences are often formatting-only, but should still be reviewed in context. For example, HTTPS://EXAMPLE.COM:443/page and https://example.com/page use different letter casing and spell out HTTPS's default port. Browsers and URL parsers normalize these aspects, and hostnames are case-insensitive. By contrast, do not treat a changed path or query as harmless just because the page appears to load.

Encoding needs special care. %7e and %7E use different hex-letter casing for the same percent-encoded byte. An encoded character and its literal form may also be equivalent in some URL contexts, but server routing and application behavior matter. Encoded slashes, for instance, can be handled differently by servers and frameworks. Compare the exact value, then verify the destination behavior rather than applying broad decoding or replacement rules.

A repeatable comparison workflow

Use the same process for a single link or a release checklist:

  1. Collect the source and expected URLs. Copy them from their actual locations, such as the built page, sitemap, CMS export, or test fixture. Record the page or test case each URL belongs to.
  2. Compare the raw strings. This catches every change, including differences a parser may normalize. Keep both original values in the QA record.
  3. Compare parsed components. Check scheme, host, port, path, query, and fragment. Note which component differs instead of writing only “URL mismatch.”
  4. Inspect query parameters without discarding order or duplicates. Check names and values, and watch for missing, repeated, reordered, or newly added parameters. Some applications and signed URLs depend on order or exact serialization.
  5. Check the destination in the release environment. Follow the link and verify the final page, redirects, and expected page state. A matching URL does not prove that the destination works.
  6. Classify and record the result. Mark the difference as expected, formatting-only with evidence, or a defect to fix. Include the test case and observed result so another reviewer can reproduce it.
  7. Repeat across representative cases. Include URLs with query strings, fragments, encoded characters, and trailing slashes—not just the homepage.

A small JavaScript helper can make component differences easier to inspect. It accepts absolute URLs and reports both the raw-string result and parsed components. It intentionally does not sort query parameters or declare differences harmless.

function compareUrls(source, expected) {
  let a;
  let b;

  try {
    a = new URL(source);
    b = new URL(expected);
  } catch (error) {
    throw new TypeError(`Enter two valid absolute URLs: ${error.message}`);
  }

  const components = [
    ["scheme", a.protocol, b.protocol],
    ["host", a.hostname, b.hostname],
    ["port", a.port, b.port],
    ["path", a.pathname, b.pathname],
    ["query", a.search, b.search],
    ["fragment", a.hash, b.hash],
  ];

  return {
    rawMatch: source === expected,
    differences: components
      .filter(([, left, right]) => left !== right)
      .map(([component, left, right]) => ({ component, source: left, expected: right })),
  };
}

const result = compareUrls(
  "https://example.com/products?color=blue#details",
  "https://example.com/products/?color=green#details"
);

console.log(result);

The result reports differences in path and query, while rawMatch is false. The URL API parses and normalizes some URL details, so retain the raw strings when exact spelling or serialization matters. This helper is a comparison aid, not a check that a URL is reachable or that its behavior is correct.

Edge cases to include in release checks

  • Encoded characters: Compare exact percent-encoded sequences. Avoid decoding the entire URL before comparison; doing so can hide meaningful differences or change delimiters.
  • Trailing slashes: Test both forms against the deployed site. Record whether one redirects and whether the final destination is expected.
  • Parameter changes: Check additions, removals, and changed values. Treat repeated keys, such as tag=a&tag=b, as potentially meaningful.
  • Parameter order: ?a=1&b=2 and ?b=2&a=1 may be treated alike by an application, but do not assume that. Order can matter to custom handlers, caches, or signature validation.
  • Fragments: Confirm that the target section exists or that the client-side route works. Fragments do not reach the server in the HTTP request.
  • Default ports and casing: A parser may normalize host casing and omit a scheme's default port. Still review raw changes if exact output is part of the requirement.
  • Redirects: Compare the link as authored and the final URL after navigation. A redirect may be expected, but it can also conceal a wrong destination.

Common Mistakes

  • Comparing only the full string. This shows that something changed, but not whether the change is in the host, path, or parameter.
  • Sorting or deduplicating query parameters automatically. This can conceal order changes or repeated keys that the application uses.
  • Assuming a trailing slash never matters. Confirm the site's behavior instead of applying a blanket rule.
  • Decoding before comparison. Decoding reserved characters can alter how a URL is interpreted and obscure the original difference.
  • Treating a successful page load as proof of correctness. A URL can load while pointing to the wrong page, dropping tracking data, or relying on an unexpected redirect.
  • Ignoring the fragment. It may not be sent to the server, but it can still affect the browser's destination or a client-side route.

Best Practices

  • Save the exact source and expected URLs in your test evidence.
  • Compare components and raw strings; use each view for what it reveals.
  • Keep an explicit expected outcome for redirects, trailing slashes, parameter order, and encoding.
  • Test representative edge cases in the release environment, especially when routing or URL generation has changed.
  • Make the final decision based on site behavior and requirements, not a generic URL canonicalization rule.

Conclusion

A reliable URL QA check identifies which component changed, then verifies whether that change is expected for the site. Preserve the original strings, inspect edge cases deliberately, and confirm important destinations in the release environment before approving the links.