Your Static Web App didn’t go down at midnight. That’s the trap. As of today, 10 November 2026, managed Functions APIs running .NET 8 or .NET 9 keep serving traffic exactly as they did yesterday, same cold-start, same latency, same 200s. What changed is invisible: no more security patches to the runtime, and any support ticket you open that traces back to a .NET 8 or 9 host gets closed with a link to the lifecycle page. You’re flying, but nobody’s in the tower anymore.
This is narrow, so let me scope it before anyone panics. If your Static Web App serves static content and calls an external backend, a separate Function App, Container App, APIM, whatever, this notice doesn’t touch you. The thing on the clock is the managed Functions API: the /api folder that Static Web Apps builds and hosts for you, on the runtime baked into your build. .NET 6 and .NET 7 are on their own separate timelines; don’t let anyone conflate them into this sprint.
The inventory problem nobody warns you about
Here’s the first thing that bites you, and it has nothing to do with code. You cannot cleanly query the managed Functions runtime from the ARM control plane. The Static Web App resource will happily tell you its SKU, its default hostname, its linked repo. The runtime version of the managed API? That lives in your build, in the .csproj target framework and, where you’ve pinned it, the platform.apiRuntime value in staticwebapp.config.json. It is not a first-class property you can trust from az staticwebapp show.
So the audit is two passes. First, enumerate every Static Web App you own, across every subscription, so you know the universe:
Hold that list. Now the pass that actually matters, grep the source, because that’s where the truth is. If your repos are on disk or in a CI checkout:
Cross-reference the repo URLs from the first command against the hits from the second. The intersection is your actual exposure. Everything else is noise. If that feels like more archaeology than a cloud platform should demand in 2026, yes. The managed-API abstraction is lovely until the day you need to inventory it, and then the abstraction is the problem.
Sort the survivors by blast radius, not alphabetically. Anything that validates tokens, touches a database, or exposes a public unauthenticated endpoint goes to the top. An internal dashboard that reads a cache can wait a week. A /api/auth callback on the public internet, running a runtime that will never see another CVE patch, cannot.
What actually breaks
Three things break on this migration, and none of them are the framework version string. They’re the same three that bite every isolated-worker .NET upgrade, this notice just changed the version numbers. None of them shows up as a red X in the build log, which is exactly why they’re on this list.
One: the isolated worker packages drift and the host won’t load the function. Static Web Apps managed Functions run the isolated worker model. Bump <TargetFramework> to net10.0 without also moving Microsoft.Azure.Functions.Worker and Microsoft.Azure.Functions.Worker.Sdk to versions that know about .NET 10, and you get a build that compiles and a deploy that silently indexes zero functions. No error on the portal. Just a 404 where your API used to be. The fix is dull and non-negotiable: pin the worker SDK packages to their current releases in the same commit as the framework bump, or don’t bother.
Two: auth token validation gets stricter. Any app doing JWT validation on an older Microsoft.Identity.Web is exposed here. On .NET 10 the transitive Microsoft.IdentityModel stack moves forward, and a token that used to sail through can start failing on an issuer or audience check that was previously laxer. That’s arguably the runtime doing its job. It’s also a 500 in production at an inconvenient hour if you find out by shipping. Update the identity packages deliberately and read their changelog, don’t let the restore pick versions for you.
Three: JSON serialization changes shape. Any response that leans on default System.Text.Json behaviour can come back subtly different across a major-version jump, property casing, or a nullable field that now serializes where it used to be omitted. Nothing throws. The frontend just quietly renders empty. These are the migrations that pass every test you have and fail the one you didn’t write. Diff the actual HTTP responses before and after. Byte for byte where you can.
Three ways up the hill
You have three migration paths. They are not equal, and the vendor doc that lists them as a flat bullet list is doing you no favours. Score them on the things that matter at 2 a.m.: testability, rollback safety, and blast radius.
Round one, can you test it before it’s live?
The in-place bump (change the runtime, push to main, watch) is untestable by definition, production is your test. The full retarget (bump framework and packages, run it locally, then deploy) lets you catch all three of the breakages above on your laptop:
The side-by-side cutover tests it in a real Azure environment without touching production, using Static Web Apps’ own preview environments, open a PR and the platform stands up an isolated staging URL built from that branch. Round one: full retarget and side-by-side tie; in-place loses badly.
Round two, rollback safety
This is where the in-place bump earns its reputation. Your rollback is “revert the commit and wait for the next build,” which means your recovery time is a full CI/CD cycle while the broken version serves live. The full retarget is the same story, one production slot, one version, mean-time-to-recovery measured in build minutes. The side-by-side approach wins outright: you validate the preview environment, and promotion is a traffic decision, not a rebuild. If .NET 10 misbehaves, you simply don’t promote, and production never knew. Round two: side-by-side, unambiguously.
Round three, blast radius and effort
In-place is one config change and a push, minimal effort, maximum exposure, the whole app at once. Side-by-side costs you a staging environment and a promotion step per app, which across forty Static Web Apps is real work. The full retarget sits in the middle: honest effort, contained to one app, no extra infrastructure. For a long tail of low-risk internal apps, spinning a parallel environment for each is ceremony you don’t need. Round three: full retarget on effort, side-by-side on blast radius, call it split.
The verdict
For anything on the public internet, anything handling auth, anything touching data: side-by-side via a preview environment. It’s the only option where a bad migration is a non-event instead of an incident, and those are exactly the apps where an incident is expensive. The extra staging step pays for itself the first time .NET 10 rejects a token you expected it to accept.
The one case I’d pick the “loser”: a genuinely internal, low-traffic app with no auth and no sensitive data, and a repo you can redeploy in your sleep. There, the full retarget, framework bump, package bump, func start locally, deploy, diff the responses, is the right amount of process. The pure in-place bump I’d keep for exactly one scenario: you already validated the identical change in a preview environment and you’re promoting it. As a standalone strategy, pushing a runtime change straight to main and watching the logs is not a migration. It’s a bet.
The apps didn’t break at midnight. They’ll break in March, when a serialization quirk ships a wrong number into a report, or a CVE lands on a host that no longer gets patched and your scanner lights up red on a runtime Microsoft stopped caring about in November. Do the grep today. The migration can wait a week; knowing your exposure cannot.