{"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":"Error","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/3600ca084e9c8b5f4174fc5e747f4c52d2100806/src/Components/Web.JS/src/Services/NavigationEnhancement.ts#L223-L259","documentation":"Enhanced navigation in Blazor intercepts form submissions and link clicks and fetches the response via the Fetch API. When the server responds with a redirect to an external origin, the browser follows it in no-cors mode and the response becomes 'opaque', hiding the destination. For GET requests Blazor transparently falls back to a full page load, but for non-GET (POST/PUT/etc.) requests it cannot safely replay the form, so it throws this error rather than silently doing the wrong thing.","triggerScenarios":"Thrown at line 241 when response.type === 'opaque' (external-origin redirect detected) AND isGetRequest is false — i.e. an enhanced form POST reached an endpoint that 301/302-redirected to a different origin.","commonSituations":"A Blazor enhanced form whose POST handler redirects to an external payment provider / SSO / third-party URL; mixing enhanced navigation with forms that hand off to external services; a [HttpPost] endpoint that returns Redirect(...) to an absolute external URL.","solutions":["Do not enable enhanced navigation (data-enhance / Blazor enhanced form) on forms that may redirect to an external origin; let them do a normal full POST.","Change the form's method to 'get' if appropriate, or remove its action so Blazor's enhanced nav is bypassed for that submit.","Handle external redirects as a two-step: POST to your own endpoint, then return a client-side trigger (e.g. a link) the user clicks to leave the app.","If you must POST then go external, perform the redirect from the client after the fetch resolves instead of via a server 30x."],"exampleFix":"<!-- before: enhanced form posts then redirects externally -->\n<form method=\"post\" data-enhance=\"true\" action=\"/pay\">...</form>\n<!-- after: do not enhance forms that leave the origin -->\n<form method=\"post\" action=\"/pay\">...</form>","handlingStrategy":"validation","validationCode":"// Before enhancing a form, ensure its POST never redirects externally\nfunction isSafeToEnhance(form: HTMLFormElement): boolean {\n  // Heuristic: do not enhance forms whose handler may hand off externally\n  const externalHandoff = form.dataset['externalHandoff'];\n  return form.method.toLowerCase() !== 'post' || externalHandoff !== 'true';\n}\nif (!isSafeToEnhance(form)) form.removeAttribute('data-enhance');","typeGuard":null,"tryCatchPattern":"try {\n  await enhancedNavSubmit(form);\n} catch (e) {\n  if (/external origin/i.test((e as Error).message)) {\n    // fall back to a non-enhanced submit\n    form.removeAttribute('data-enhance');\n    (form as HTMLFormElement & { submit(): void }).submit();\n  } else throw e;\n}","preventionTips":["Do not mark forms that POST to endpoints which redirect externally as enhanced.","For external handoffs (payments, SSO), perform the redirect from the client after a successful fetch.","Prefer GET or no-action forms when using enhanced navigation.","Document which endpoints may redirect externally in your routing layer."],"tags":["blazor","enhanced-navigation","forms","redirect","cors","external-origin"],"backgroundTag":null,"analyzedSha":"3600ca084e9c8b5f4174fc5e747f4c52d2100806","analyzedAt":"2026-08-11T16:32:30.678Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}