{"record":{"id":"c4cfeda28086916a","repo":"dotnet/aspnetcore","slug":"cannot-perform-enhanced-form-submission-that-chang","errorCode":null,"errorMessage":"Cannot perform enhanced form submission that changes the URL (except via a redirection), because then back/forward would not work. Either remove this form's 'action' attribute, or change its method to 'get', or do not mark it as enhanced.\nOld URL: ${location.href}\nNew URL: ${response.url}","messagePattern":"Cannot perform enhanced form submission that changes the URL \\(except via a redirection\\), because then back/forward would not work\\. Either remove this form's 'action' attribute, or change its method to 'get', or do not mark it as enhanced\\.\nOld URL: (.+?)\nNew URL: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/Components/Web.JS/src/Services/NavigationEnhancement.ts","lineNumber":362,"sourceCode":"    // has since started). So finally, recreate the native \"scroll to hash\" behavior.\n    const hashPosition = internalDestinationHref.indexOf('#');\n    if (hashPosition >= 0) {\n      const hash = internalDestinationHref.substring(hashPosition + 1);\n      const targetElem = document.getElementById(hash);\n      targetElem?.scrollIntoView();\n    }\n\n    performingEnhancedPageLoad = false;\n    navigationEnhancementCallbacks.enhancedNavigationCompleted();\n\n    // For non-GET requests, the destination has to be the same URL you're already on, or result in a redirection\n    // (post/redirect/get). You're not allowed to POST to a different URL without redirecting, because then back/forwards\n    // won't work - we can't recreate the \"Resubmit form?\" behavior.\n    // See https://github.com/dotnet/aspnetcore/issues/50945\n    // The reason we delay throwing until after SSR completes is that SSR might include a redirection signal. If we get\n    // here without navigating away, it's an error.\n    if (isNonRedirectedPostToADifferentUrlMessage) {\n      throw new Error(isNonRedirectedPostToADifferentUrlMessage);\n    }\n  }\n}\n\nasync function getResponsePartsWithFraming(responsePromise: Promise<Response>, abortSignal: AbortSignal, onInitialDocument: (response: Response, initialDocumentText: string) => void, onStreamingElement: (streamingElementMarkup) => void) {\n  let response: Response;\n\n  try {\n    response = await responsePromise;\n\n    if (!response.body) { // Not sure how this can happen, but the TypeScript annotations suggest it can\n      onInitialDocument(response, '');\n      return;\n    }\n\n    const frameBoundary = response.headers.get('ssr-framing');\n    if (!frameBoundary) {\n      // Shouldn't happen, but perhaps some proxy stripped the headers. In that case we just won't respect streaming and will","sourceCodeStart":344,"sourceCodeEnd":380,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/3600ca084e9c8b5f4174fc5e747f4c52d2100806/src/Components/Web.JS/src/Services/NavigationEnhancement.ts#L344-L380","documentation":"Enhanced navigation cannot support a non-GET form submission that lands on a different URL without a redirection, because the browser history entry would be un-recreatable: there is no way to reproduce a 'resubmit this POST?' prompt on back/forward. The check is intentionally delayed until after SSR completes (in case SSR emits a redirect signal), then throws if no redirect occurred. Reference: dotnet/aspnetcore#50945.","triggerScenarios":"Thrown at line 362 via the isNonRedirectedPostToADifferentUrlMessage string after enhanced SSR completes, when the request was a non-GET (POST/PUT) to a different URL than the current page and the response was not a redirect that the browser followed.","commonSituations":"An enhanced form with an explicit action attribute pointing to a different URL than the current page, posting without a post/redirect/get pattern; action='/search' on a page at '/' with method=post and data-enhance.","solutions":["Remove the form's action attribute so it posts back to the current URL (Blazor-enhanced POST to the same URL is allowed).","Change the form method to 'get'.","Remove data-enhance / do not mark the form as enhanced.","Implement a post/redirect/get: POST returns a 303 See Other to the destination so response.redirected is true."],"exampleFix":"<!-- before -->\n<form method=\"post\" action=\"/other-url\" data-enhance=\"true\">\n<!-- after: post to the same URL -->\n<form method=\"post\" data-enhance=\"true\"> <!-- no action, posts to current page -->","handlingStrategy":"validation","validationCode":"// Do not enhance POST forms whose action differs from the current URL unless they redirect\nfunction shouldEnhancePost(form: HTMLFormElement): boolean {\n  const method = (form.getAttribute('method') || 'get').toLowerCase();\n  if (method !== 'post') return true;\n  const action = form.getAttribute('action');\n  if (!action) return true; // posts to current URL\n  const here = window.location.pathname + window.location.search;\n  return new URL(form.action, window.location.href).pathname + new URL(form.action, window.location.href).search === here;\n}\nif (!shouldEnhancePost(form)) form.removeAttribute('data-enhance');","typeGuard":null,"tryCatchPattern":"try {\n  await enhancedNavSubmit(form);\n} catch (e) {\n  if (/changes the URL/i.test((e as Error).message)) {\n    // fall back: full POST\n    form.removeAttribute('data-enhance');\n    (form as HTMLFormElement & { submit(): void }).submit();\n  } else throw e;\n}","preventionTips":["Omit the action attribute on enhanced POST forms so they post to the current URL.","Use GET for forms that should change the URL.","Implement post/redirect/get (303) for cross-URL POSTs you still want enhanced.","Only mark a form as enhanced when you control both its action and its response."],"tags":["blazor","enhanced-navigation","forms","history","post-redirect-get"],"backgroundTag":null,"analyzedSha":"3600ca084e9c8b5f4174fc5e747f4c52d2100806","analyzedAt":"2026-08-11T16:32:30.678Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}