dotnet/aspnetcore · error · Error

Cannot perform enhanced form submission that changes the…

Error message

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.
Old URL: ${location.href}
New URL: ${response.url}

What it means

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.

Solutions

  1. Remove the form's action attribute so it posts back to the current URL (Blazor-enhanced POST to the same URL is allowed).
  2. Change the form method to 'get'.
  3. Remove data-enhance / do not mark the form as enhanced.
  4. Implement a post/redirect/get: POST returns a 303 See Other to the destination so response.redirected is true.

Example fix

<!-- before -->
<form method="post" action="/other-url" data-enhance="true">
<!-- after: post to the same URL -->
<form method="post" data-enhance="true"> <!-- no action, posts to current page -->
Defensive patterns

Strategy: validation

Validate before calling

// Do not enhance POST forms whose action differs from the current URL unless they redirect
function shouldEnhancePost(form: HTMLFormElement): boolean {
  const method = (form.getAttribute('method') || 'get').toLowerCase();
  if (method !== 'post') return true;
  const action = form.getAttribute('action');
  if (!action) return true; // posts to current URL
  const here = window.location.pathname + window.location.search;
  return new URL(form.action, window.location.href).pathname + new URL(form.action, window.location.href).search === here;
}
if (!shouldEnhancePost(form)) form.removeAttribute('data-enhance');

Try / catch

try {
  await enhancedNavSubmit(form);
} catch (e) {
  if (/changes the URL/i.test((e as Error).message)) {
    // fall back: full POST
    form.removeAttribute('data-enhance');
    (form as HTMLFormElement & { submit(): void }).submit();
  } else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of dotnet/aspnetcore@3600ca084e (2026-08-11). Data as JSON: /api/errors/c4cfeda28086916a. Report an issue: GitHub.

Appendix: source

Thrown at src/Components/Web.JS/src/Services/NavigationEnhancement.ts:362

    // has since started). So finally, recreate the native "scroll to hash" behavior.
    const hashPosition = internalDestinationHref.indexOf('#');
    if (hashPosition >= 0) {
      const hash = internalDestinationHref.substring(hashPosition + 1);
      const targetElem = document.getElementById(hash);
      targetElem?.scrollIntoView();
    }

    performingEnhancedPageLoad = false;
    navigationEnhancementCallbacks.enhancedNavigationCompleted();

    // For non-GET requests, the destination has to be the same URL you're already on, or result in a redirection
    // (post/redirect/get). You're not allowed to POST to a different URL without redirecting, because then back/forwards
    // won't work - we can't recreate the "Resubmit form?" behavior.
    // See https://github.com/dotnet/aspnetcore/issues/50945
    // The reason we delay throwing until after SSR completes is that SSR might include a redirection signal. If we get
    // here without navigating away, it's an error.
    if (isNonRedirectedPostToADifferentUrlMessage) {
      throw new Error(isNonRedirectedPostToADifferentUrlMessage);
    }
  }
}

async function getResponsePartsWithFraming(responsePromise: Promise<Response>, abortSignal: AbortSignal, onInitialDocument: (response: Response, initialDocumentText: string) => void, onStreamingElement: (streamingElementMarkup) => void) {
  let response: Response;

  try {
    response = await responsePromise;

    if (!response.body) { // Not sure how this can happen, but the TypeScript annotations suggest it can
      onInitialDocument(response, '');
      return;
    }

    const frameBoundary = response.headers.get('ssr-framing');
    if (!frameBoundary) {
      // Shouldn't happen, but perhaps some proxy stripped the headers. In that case we just won't respect streaming and will

View on GitHub (pinned to 3600ca084e)