{"record":{"id":"fbbcd17f3ae2e583","repo":"microsoft/playwright","slug":"new-url-must-have-same-protocol-as-overridden-url","errorCode":null,"errorMessage":"New URL must have same protocol as overridden URL","messagePattern":"New URL must have same protocol as overridden URL","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/playwright-core/src/server/network.ts","lineNumber":442,"sourceCode":"    const requestUrl = new URL(this._request.url());\n    if (!requestUrl.protocol.startsWith('http'))\n      return;\n    if (requestUrl.origin === origin.trim())\n      return;\n    const corsHeader = headers.find(({ name }) => name === 'access-control-allow-origin');\n    if (corsHeader)\n      return;\n    headers.push({ name: 'access-control-allow-origin', value: origin });\n    headers.push({ name: 'access-control-allow-credentials', value: 'true' });\n    headers.push({ name: 'vary', value: 'Origin' });\n  }\n\n  async continue(overrides: channels.RouteContinueParams) {\n    if (overrides.url) {\n      const newUrl = new URL(overrides.url);\n      const oldUrl = new URL(this._request.url());\n      if (oldUrl.protocol !== newUrl.protocol)\n        throw new Error('New URL must have same protocol as overridden URL');\n    }\n    if (overrides.headers) {\n      // Filter out forbidden headers from overrides - they cannot be overridden\n      // and will be passed as-is from the original request\n      overrides.headers = applyHeadersOverrides(this._request._headers, overrides.headers);\n    }\n    overrides = this._request._applyOverrides(overrides);\n\n    const nextHandler = this._futureHandlers.shift();\n    if (nextHandler) {\n      this._currentHandler = nextHandler;\n      nextHandler(this, this._request);\n      return;\n    }\n\n    if (!overrides.isFallback)\n      this._request._context.emit(BrowserContext.Events.RequestContinued, this._request);\n    this._startHandling();","sourceCodeStart":424,"sourceCodeEnd":460,"githubUrl":"https://github.com/microsoft/playwright/blob/c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6/packages/playwright-core/src/server/network.ts#L424-L460","documentation":"Thrown by Route.continue() when the overridden URL has a different protocol than the original request URL. The method constructs new URL objects for both and compares .protocol. Changing protocols (e.g., http to https or vice versa) is not permitted because it can break CORS, mixed-content policies, and the browser's security model. Note: changing the hostname or path is allowed.","triggerScenarios":"Calling route.continue({ url: newUrl }) in a page.route() handler where newUrl's protocol differs from the intercepted request's protocol. For example, intercepting an http:// request and continuing to an https:// URL, or vice versa. This commonly occurs when normalizing URLs or redirecting from insecure to secure endpoints.","commonSituations":"Test mock redirects http requests to https endpoints for consistency. Proxying to a different protocol server. Stripping or adding TLS via route override. URL construction bug that drops or changes the protocol scheme.","solutions":["Match the protocol of the original request URL when overriding: use the same http:// or https:// scheme.","Use route.fulfill() with a redirect response (status 301/302 + Location header) instead of route.continue() if a protocol change is needed.","Fix URL construction to preserve the protocol: new URL(path, originalUrl).toString()."],"exampleFix":"// before\nawait route.continue({ url: 'https://api.example.com/data' }); // original was http://\n\n// after — same protocol\nawait route.continue({ url: 'http://api.example.com/data' });\n\n// or — fulfill with redirect\nawait route.fulfill({\n  status: 301,\n  headers: { location: 'https://api.example.com/data' }\n});","handlingStrategy":"validation","validationCode":"function validateOverrideUrl(originalUrl, overrideUrl) {\n  const oldProto = new URL(originalUrl).protocol;\n  const newProto = new URL(overrideUrl).protocol;\n  if (oldProto !== newProto)\n    throw new Error(`Protocol mismatch: ${oldProto} -> ${newProto}. Use route.fulfill with redirect.`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await route.continue({ url: newUrl });\n} catch (e) {\n  if (e.message === 'New URL must have same protocol as overridden URL') {\n    // Fulfill with redirect instead\n    await route.fulfill({ status: 302, headers: { location: newUrl } });\n  } else throw e;\n}","preventionTips":["Match the protocol scheme when overriding URLs in route.continue().","Use route.fulfill() with a redirect status for protocol changes.","Construct override URLs from the original URL to preserve the scheme."],"tags":["route","continue","url-override","protocol","network"],"backgroundTag":null,"analyzedSha":"c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6","analyzedAt":"2026-08-12T07:26:36.950Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}