{"record":{"id":"461c6c49ca5cdc3b","repo":"dotnet/aspnetcore","slug":"enhanced-navigation-does-not-support-making-a-non","errorCode":null,"errorMessage":"Enhanced navigation does not support making a non-GET request to an endpoint that redirects to an external origin. Avoid enabling enhanced navigation for form posts that may perform external redirections.","messagePattern":"Enhanced navigation does not support making a non-GET request to an endpoint that redirects to an external origin\\. Avoid enabling enhanced navigation for form posts that may perform external redirections\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/Components/Web.JS/src/Services/NavigationEnhancement.ts","lineNumber":241,"sourceCode":"  }, fetchOptions));\n  let isNonRedirectedPostToADifferentUrlMessage: string | null = null;\n  await getResponsePartsWithFraming(\n    responsePromise, abortSignal,\n    (response, initialContent) => {\n      const isGetRequest = !fetchOptions?.method || fetchOptions.method === 'get';\n      const isSuccessResponse = response.status >= 200 && response.status < 300;\n\n      // For true 301/302/etc redirections to external URLs, we'll receive an opaque response\n      // (even if it has CORS enabled, since we passed no-cors), and the browser won't disclose\n      // the target URL to JS code. We must therefore retry as a non-enhanced-nav page load to reach\n      // the destination. This also has the benefit that we can be certain not to introduce content\n      // from an external origin into the DOM here.\n      if (response.type === 'opaque') {\n        if (isGetRequest) {\n          retryEnhancedNavAsFullPageLoad(internalDestinationHref);\n          return;\n        } else {\n          throw new Error('Enhanced navigation does not support making a non-GET request to an endpoint that redirects to an external origin. Avoid enabling enhanced navigation for form posts that may perform external redirections.');\n        }\n      }\n\n      if (isSuccessResponse && response.headers.get('blazor-enhanced-nav') !== 'allow') {\n        // This appears to be a non-Blazor-Endpoint success response. We don't want to use enhanced nav\n        // because the content we receive is not designed to be patched into an existing frame,\n        // and may be incompatible with the Blazor JS that's already here.\n        // The reason we don't apply the same logic for non-success responses is that:\n        //  - We don't want to retry as then developers will get double-failures in logs\n        //  - We really want to show error pages to avoid losing vital debugging info\n        // ... and since error pages can be considered terminally fatal, we don't have to worry about\n        // whether the page has complex client-side behaviors that are incompatible with our JS.\n        if (isGetRequest) {\n          retryEnhancedNavAsFullPageLoad(internalDestinationHref);\n          return;\n        } else {\n          throw new Error('Enhanced navigation does not support making a non-GET request to a non-Blazor endpoint. Avoid enabling enhanced navigation for forms that post to a non-Blazor endpoint.');\n        }","sourceCodeStart":223,"sourceCodeEnd":259,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/294cab2f9b2e03af6b953820c7ab497c3c8b7ad9/src/Components/Web.JS/src/Services/NavigationEnhancement.ts#L223-L259","documentation":"Thrown by performEnhancedPageLoad (NavigationEnhancement.ts:241) during an enhanced (fetch-based) form submission whose response is opaque — which means the server redirected a non-GET (POST/PUT/etc.) request to an external origin. Because the request used mode:'no-cors' to avoid leaking cross-origin content into the DOM, an external redirect on a non-GET cannot be retried as a full page load safely, so Blazor aborts with this error rather than silently failing.","triggerScenarios":"An enhanced form (data-enhance) with method POST/PUT/etc. whose endpoint returns a 30x redirect to a different origin (different scheme/host/port).","commonSituations":"A Blazor form posting to an endpoint that redirects to a third-party payment provider, SSO, or external service; OAuth callbacks; payment gateways; cross-domain redirects after form submission.","solutions":["Remove data-enhance from forms that may redirect externally (let them do a native full POST).","Return the external URL via the blazor-enhanced-nav-redirect-location header instead of an HTTP redirect, so Blazor can location.replace cleanly.","Change the form method to 'get' if semantics allow.","Handle the redirect server-side and surface a normal Blazor response."],"exampleFix":"<!-- before -->\n<form method=\"post\" data-enhance action=\"/pay\">\n\n<!-- after: external redirect not safe for enhanced nav -->\n<form method=\"post\" action=\"/pay\">\n<!-- server returns header 'blazor-enhanced-nav-redirect-location: https://gateway.example' -->","handlingStrategy":"validation","validationCode":"function isSafeForEnhancedPost(form: HTMLFormElement): boolean {\n  const get = (form.getAttribute('method') || 'get').toLowerCase();\n  return get === 'get'; // POSTs that may redirect externally are unsafe for enhanced nav\n}","typeGuard":"function formMayRedirectExternally(form: HTMLFormElement): boolean {\n  // conservative: any POST form whose action host differs from current origin\n  try {\n    const u = new URL(form.getAttribute('action') || form.action, document.baseURI);\n    return u.origin !== location.origin;\n  } catch { return true; }\n}","tryCatchPattern":"try {\n  await performEnhancedPageLoad(url, false, fetchOptions);\n} catch (e) {\n  if (/redirects to an external origin/.test((e as Error).message)) {\n    // fall back: submit the form natively without enhanced nav\n  } else { throw e; }\n}","preventionTips":["Do not mark externally-redirecting POST forms with data-enhance.","Use the blazor-enhanced-nav-redirect-location header for external redirects.","Prefer 'get' forms when semantics allow."],"tags":["blazor","enhanced-nav","forms","redirect","cors","external","navigation"],"analyzedSha":"294cab2f9b2e03af6b953820c7ab497c3c8b7ad9","analyzedAt":"2026-08-06T20:08:02.189Z","schemaVersion":2},"datasetVersion":"2026-08-06T23:17:07.152Z"}