{"record":{"id":"903c0dafa2278553","repo":"angular/angular","slug":"invalid-url","errorCode":"INVALID_URL","errorMessage":"Invalid URL: ${urlStr}","messagePattern":"Invalid URL: (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"packages/platform-server/src/url.ts","lineNumber":83,"sourceCode":"    resolved = new URL(urlStr);\n  } catch {}\n  const {allowProtocolRelative = false, allowOriginChange = true} = options;\n\n  if (resolved) {\n    if (originUrl && !isSafeOriginChange(resolved, originUrl, urlStr, allowOriginChange)) {\n      throwSuspiciousUrlError(urlStr);\n    }\n\n    return resolved;\n  }\n\n  // We identify and throw on malformed absolute URLs (like double port).\n  // Per the WHATWG URL standard, parsing an input starting with a scheme (like 'http:') against\n  // a standard base (like 'http://fake') ignores the base argument and parses strictly as an\n  // absolute URL. Since it is malformed, the native URL constructor will throw a validation\n  // error. Standard relative/protocol-relative paths parse successfully, allowing the flow to continue.\n  if (!URL.canParse(urlStr, 'http://fake')) {\n    throw new RuntimeError(\n      RuntimeErrorCode.INVALID_URL,\n      typeof ngDevMode === 'undefined' || ngDevMode ? `Invalid URL: ${urlStr}` : urlStr,\n    );\n  }\n\n  if (!originUrl) {\n    return null;\n  }\n\n  // Check if we have a legitimate protocol-relative URL (starts with '//' and not a duplicate/backslash bypass)\n  // and we are configured to allow and preserve standard cross-origin protocol-relative requests.\n  if (urlStr.startsWith('//')) {\n    if (!allowProtocolRelative) {\n      throw new RuntimeError(\n        RuntimeErrorCode.PROTOCOL_RELATIVE_URL_NOT_ALLOWED,\n        typeof ngDevMode === 'undefined' || ngDevMode\n          ? `Protocol relative URLs are not allowed in this context. URL: ${urlStr}`\n          : urlStr,","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/angular/angular/blob/51cb07e98081ab7e4e84a9e0949266a8e19cca84/packages/platform-server/src/url.ts#L65-L101","documentation":"resolveUrl() in platform-server first tries `new URL(urlStr)`; if that fails it checks `URL.canParse(urlStr, 'http://fake')` to detect inputs that carry a scheme (e.g. `http:`) but are malformed absolute URLs, such as a double port `http://host:8080:9090/`. Per the WHATWG spec such input parses strictly against the base and still fails, so the flow throws INVALID_URL rather than silently resolving a broken absolute URL against the app origin.","triggerScenarios":"Passing a scheme-bearing malformed URL to any resolveUrl consumer: the `url` option of renderApplication/renderModule, INITIAL_CONFIG `{url: ...}` for ServerPlatformLocation, or the platform-server relative-URL interceptor receiving e.g. `http://host:8080:8080/path`, `https://exa mple.com`, or `https://[bad`.","commonSituations":"Reverse proxies forwarding malformed Host or X-Forwarded-Host headers that produce double-port URLs; environment variables or config files supplying a malformed absolute URL; string concatenation bugs when assembling request URLs on the server.","solutions":["Fix the URL string itself — remove the duplicate port/whitespace/invalid characters (the message prints the exact offending URL)","Validate with `URL.canParse(url)` (or try `new URL(url)`) before passing the URL into renderApplication/INITIAL_CONFIG","Check proxy header handling (Host, X-Forwarded-Host, X-Forwarded-Port) that builds the URL server-side"],"exampleFix":"// before\nconst url = `${protocol}//${host}:${port}:${altPort}/app`; // double port\nawait renderApplication(bootstrap, {url}); // Invalid URL\n\n// after\nconst url = `${protocol}//${host}:${port}/app`;\nif (!URL.canParse(url)) throw new Error(`Bad request URL: ${url}`);\nawait renderApplication(bootstrap, {url});","handlingStrategy":"validation","validationCode":"// Validate before passing to renderApplication / INITIAL_CONFIG\nfunction isParseableAbsoluteUrl(url: string): boolean {\n  return URL.canParse(url);\n}\n\nif (!isParseableAbsoluteUrl(requestUrl)) {\n  throw new Error(`Refusing to render: malformed request URL ${JSON.stringify(requestUrl)}`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await renderApplication(bootstrap, {url});\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('Invalid URL:')) {\n    // log the offending URL and reject the request with 400\n    respond400(`Malformed request URL: ${url}`);\n  } else {\n    throw e;\n  }\n}","preventionTips":["Never assemble absolute URLs by string concatenation of proxy headers; use new URL() to build them","Validate inbound request URLs with URL.canParse at the server request handler boundary","Log the exact failing URL (the error message embeds it) before discarding the error"],"tags":["ssr","url","validation","whatwg-url"],"backgroundTag":"invalid-url-format","analyzedSha":"51cb07e98081ab7e4e84a9e0949266a8e19cca84","analyzedAt":"2026-08-22T07:04:54.531Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}