Skip to content

JavaScript Tutorial JavaScript Security Basics

URL Validation and Safe Links

Lesson 230 of 258 2 min read By Deepak Paswan Updated Oct 4, 2026

URLs that come from users or from data (a profile website, a “return to” parameter, an API field) can be used in unsafe ways. Validate them before you put them into a link, an image or a redirect.

Problem 1: dangerous protocols

A link’s href does not have to start with http. Some schemes, such as javascript:, can run code when clicked.

index.html
HTML
<!-- If userUrl is "javascript:..." this link runs script -->
<a href="${userUrl}">Visit my site</a>

The fix: parse it and allow only what you expect

Use the URL constructor, and check the protocol against an allow list (a list of permitted values), instead of trying to block bad ones.

script.js
JS
function safeUrl(input, allowedProtocols = ["https:", "http:"]) {
  try {
    const url = new URL(input);
    return allowedProtocols.includes(url.protocol) ? url.href : null;
  } catch {
    return null;       // not a valid absolute URL
  }
}

console.log(safeUrl("https://example.com/page"));   // → https://example.com/page
console.log(safeUrl("javascript:doSomething()"));   // → null
console.log(safeUrl("data:text/html,hello"));        // → null
console.log(safeUrl("not a url"));                  // → null

An allow list is safer than a block list, because you cannot predict every tricky variation.

Using it

index.html
HTML
<a id="link">Visit</a>
<script>
  const link = document.querySelector("#link");
  const userSupplied = "https://example.com/profile";

  const href = safeUrl(userSupplied);
  if (href) {
    link.href = href;
    link.textContent = "Visit the website";
  } else {
    link.removeAttribute("href");
    link.textContent = "(invalid website)";
  }

  function safeUrl(input) {
    try {
      const url = new URL(input);
      return ["https:", "http:"].includes(url.protocol) ? url.href : null;
    } catch {
      return null;
    }
  }
</script>

Problem 2: open redirects

A page that takes a “where to go next” URL, such as /login?next=..., and redirects there, can be abused to send users to a malicious site that looks like yours. Only redirect to addresses on your own site.

script.js
JS
function safeRedirectTarget(next, siteOrigin = "https://example.com") {
  try {
    const url = new URL(next, siteOrigin);          // relative paths are resolved against your origin
    return url.origin === siteOrigin ? url.pathname + url.search + url.hash : "/";
  } catch {
    return "/";
  }
}

console.log(safeRedirectTarget("/account/orders"));          // → /account/orders
console.log(safeRedirectTarget("https://evil.example/login")); // → /
console.log(safeRedirectTarget("//evil.example/x"));         // → /

Comparing the parsed origin handles tricks that simple string checks miss. Redirect checks belong on the server too.

Use rel="noopener" on links with target="_blank" so the new page cannot control yours through window.opener. Modern browsers already treat _blank links as noopener by default, but writing it is a clear habit. Add noreferrer if you also do not want to send the referrer.

index.html
HTML
<a href="https://other.example" target="_blank" rel="noopener noreferrer">External link</a>

Images and other resources

URLs set as src for images, frames or scripts should also come from trusted sources. Never load a script URL from user input.

Encoding when building URLs

Use URL and URLSearchParams to add user text as parameters (URL and URLSearchParams). They encode characters correctly.

lightbulb
The principle
Parse with URL, compare against an allow list, and fall back to a safe default. Never test URLs with a few startsWith checks.