Migrating to Modern ASP.NET

This section documents ASP.NET Web Forms on .NET Framework 4.8.1, the last and permanent version of Web Forms — the page life cycle and postback model, ViewState and control state, server controls, validation controls, master pages and themes, data-bound controls, and the provider-based security model — as described by the official documentation at Microsoft Learn and the ASP.NET previous-versions archive, which are the reference these pages are written and verified against.

Web Forms receives security fixes only and has no forward path onto modern .NET (.NET Framework 4.8.1 is Microsoft’s last version of .NET Framework; Web Forms itself never shipped on .NET Core/.NET 5+). It remains supported for existing applications running on Windows but is not recommended for new development — see Choosing an ASP.NET Framework and Migrating to Modern ASP.NET for what that means in practice.

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

This section’s bibliography lists the reference material consulted while preparing these pages.

This page is the practical follow-up to the section landing page's support-status summary: given that a Web Forms application exists and needs to move forward, what are the actual options, and what does each one really cost.

The honest support picture

Repeated once more because it drives every decision on this page: there is no automatic upgrade path from Web Forms to any current ASP.NET. .NET Framework 4.8.1 receives security fixes only and has no successor; Web Forms itself never ran on .NET Core/.NET 5+ and Microsoft has not indicated it ever will. Moving off Web Forms means re-platforming to a different UI framework, full stop — the choice is which framework and how much at once, not whether a mechanical conversion tool can do it for you the way dotnet tooling can bump a .NET 8 project to .NET 10. See the ASP.NET Web Forms documentation and the .NET Framework support lifecycle for the current, authoritative statement of this.

The incremental migration path: System.Web.Adapters + YARP

For an application under active development — too large or too risky to rewrite in one release — the practical strategy is the strangler fig pattern: run the legacy .NET Framework application and a new ASP.NET Core application side by side behind a reverse proxy, and move functionality across page by page.

flowchart LR Client[Browser] --> Proxy[YARP reverse proxy] Proxy -->|"/legacy/*, not-yet-migrated pages"| WF["ASP.NET Web Forms\n(.NET Framework 4.8.1)"] Proxy -->|"migrated pages"| Core["ASP.NET Core\n(current LTS)"] WF <-->|shared Session / auth ticket\nvia System.Web.Adapters| Core
  • YARP (Yet Another Reverse Proxy, a Microsoft-built .NET library) sits in front of both applications and routes each request to whichever one currently owns that URL — as pages are migrated, routes move from the Web Forms backend to the ASP.NET Core backend, with no visible change to the end user.

  • System.Web.Adapters (the Microsoft.AspNetCore.SystemWebAdapters / matching System.Web-side package) is what makes the two halves usable together during the transition rather than as two unrelated applications — it lets the ASP.NET Core side read and write HttpContext-shaped state (Session, in particular) in a way that round-trips correctly with the still-running Web Forms side’s session store, and shares the Forms authentication ticket/cookie so a user does not have to re-log-in when a request happens to land on the other backend.

  • Each release moves a self-contained slice of pages from the Web Forms side to the ASP.NET Core side, re-implemented as Razor Pages/MVC/Blazor; YARP’s routing config is updated to match; the Web Forms application shrinks release over release until nothing routes to it and it can be retired.

This is materially more engineering effort up front than a rewrite-in-place would look like on paper, but it is what makes migrating a large, continuously-shipping application tractable without a multi-month feature freeze. See Incremental ASP.NET to ASP.NET Core migration and the YARP documentation.

The .NET Upgrade Assistant

The dotnet-upgrade-assistant global tool automates the mechanical parts of any .NET Framework → .NET migration — project file conversion (old-style .csproj to SDK-style), NuGet packages.config to PackageReference, and known API substitutions. For Web Forms specifically it does not, and cannot, convert .aspx/code-behind into Razor/Blazor — there is no mechanical equivalent for the page/control model. It is genuinely useful preparation work (getting the project file and package references into a shape that will build under a modern SDK) but does not replace the actual UI rewrite described in the rest of this page. It is considerably more effective on an MVC 5 codebase — see ASP.NET MVC's own migration coverage — because MVC’s controller/view split maps far more directly onto ASP.NET Core than Web Forms' control tree does.

Blazor Server as the closest conceptual target

Of the current ASP.NET Core UI models, Blazor Server is the closest conceptual match to Web Forms: both are server-rendered, stateful, event-driven UI models where a persistent server-side connection (a SignalR circuit, in Blazor’s case, vs. postback-per-interaction in Web Forms) carries UI events to the server and patches the client DOM in response, rather than requiring the client to manage its own application state the way a SPA or Blazor WebAssembly app does.

Web Forms concept Blazor Server equivalent

Control tree (Page, Control)

Component tree (ComponentBase, .razor files)

Page_Load

OnInitializedAsync / OnParametersSetAsync

Postback (__doPostBack, full round trip)

Server event over a persistent SignalR circuit (no full-page round trip)

ViewState

No equivalent needed — component state lives server-side in the circuit for its lifetime; see Blazor State Management for what does need explicit handling (state that must survive a circuit reconnect)

User control (.ascx)

Razor component (.razor), parameterized via [Parameter]

EnableViewState/ViewStateMode

Not applicable — state is simply server-side C# fields for the component’s lifetime

UpdatePanel partial rendering

The default and only rendering model — every component update is already incremental

Server control library (GridView, Wizard, …​)

A Blazor component library (UI Component Libraries catalogs the main options)

The migration is still a rewrite — .aspx markup and code-behind do not mechanically become .razor files — but the mental model transfers more directly than to Razor Pages/MVC, which is why teams coming from Web Forms often find Blazor Server the most comfortable landing spot conceptually, even though the actual component code is new. Microsoft’s free e-book, Blazor for ASP.NET Web Forms Developers, is written specifically for this audience and walks the same conceptual mapping in much greater depth than fits here.

In-place modernization with DotVVM as an alternative

DotVVM (open-source, dotvvm.com) is a third-party MVVM-based web framework for .NET that deliberately targets teams coming from Web Forms: it runs on both .NET Framework and current .NET, supports a more gradual in-place adoption (new pages written in DotVVM’s own markup/viewmodel model, coexisting with remaining .aspx pages in the same application, without the two-application YARP setup above), and offers server-side rendering with two-way data binding conceptually closer to Web Forms' postback model than a component-per-request Razor approach. It is a smaller ecosystem than Blazor/MVC and not a Microsoft product, which is the trade-off against its gentler migration curve — worth evaluating specifically when a full side-by-side YARP migration is judged too heavyweight for the application’s size, but not a default recommendation over Blazor Server/MVC for a green-field rewrite.

What has to be rewritten outright

Some Web Forms-era investments have no automated or even semi-automated path forward and must be redesigned:

No forward path Why

ViewState-dependent logic

Any code relying on ViewState to implicitly persist state across postbacks has to be redesigned around explicit state (component fields in Blazor, TempData/session in MVC).

HttpModule/HttpHandler

Rewritten as ASP.NET Core middleware/endpoints — the extension points exist but the API surface is unrelated (see Request Pipeline and Middleware).

Web Parts

No successor in any current framework; personalization/composable-zone UI has to be purpose-built from Razor components or a third-party dashboard library.

ASMX web services

Replaced by ASP.NET Core Web API controllers/Minimal APIs or gRPC — SOAP itself has no first-class story in ASP.NET Core.

Membership/Role/Profile password hashes

`SqlMembershipProvider’s legacy hashing (and, in older applications, unsalted or weakly salted schemes) does not map onto ASP.NET Core Identity’s password hasher —  migrating users typically means either a one-time re-hash-on-next-login shim or a forced password reset.

Realistic effort framing

Treat a Web Forms → modern ASP.NET migration as a genuine rewrite of the presentation layer, not a recompilation: the business/data-access layers, if they were kept reasonably separate from Page code-behind (see Architecture and Testing and its Model-View-Presenter discussion), are the part most likely to be reusable close to as-is; every .aspx page and its server controls are not. Sizing a migration by page count alone under-estimates it if a meaningful fraction of those pages hide business logic in code-behind rather than a presenter/service layer — that logic has to be found and extracted before it can be reused in the new UI, and that discovery work is frequently the larger, less predictable part of the effort compared to writing the new markup itself.