diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/middleware.ts b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/middleware.ts
index 6096fcfb1493..abc565f438b4 100644
--- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/middleware.ts
+++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/middleware.ts
@@ -20,5 +20,5 @@ export async function middleware(request: NextRequest) {
 
 // See "Matching Paths" below to learn more
 export const config = {
-  matcher: ['/api/endpoint-behind-middleware'],
+  matcher: ['/api/endpoint-behind-middleware', '/api/endpoint-behind-faulty-middleware'],
 };
diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/next-env.d.ts b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/next-env.d.ts
index fd36f9494e2c..725dd6f24515 100644
--- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/next-env.d.ts
+++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/next-env.d.ts
@@ -3,4 +3,4 @@
 /// 
 
 // NOTE: This file should not be edited
-// see https://nextjs.org/docs/basic-features/typescript for more information.
+// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/pages/api/async-context-edge-endpoint.ts b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/pages/api/async-context-edge-endpoint.ts
index 6dc023fdf1ed..d6a129f9e056 100644
--- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/pages/api/async-context-edge-endpoint.ts
+++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/pages/api/async-context-edge-endpoint.ts
@@ -7,21 +7,22 @@ export const config = {
 export default async function handler() {
   // Without a working async context strategy the two spans created by `Sentry.startSpan()` would be nested.
 
-  const outerSpanPromise = Sentry.withIsolationScope(() => {
-    return Sentry.startSpan({ name: 'outer-span' }, () => {
-      return new Promise(resolve => setTimeout(resolve, 300));
-    });
+  const outerSpanPromise = Sentry.startSpan({ name: 'outer-span' }, () => {
+    return new Promise(resolve => setTimeout(resolve, 300));
   });
 
-  setTimeout(() => {
-    Sentry.withIsolationScope(() => {
-      return Sentry.startSpan({ name: 'inner-span' }, () => {
+  const innerSpanPromise = new Promise(resolve => {
+    setTimeout(() => {
+      Sentry.startSpan({ name: 'inner-span' }, () => {
         return new Promise(resolve => setTimeout(resolve, 100));
+      }).then(() => {
+        resolve();
       });
-    });
-  }, 100);
+    }, 100);
+  });
 
   await outerSpanPromise;
+  await innerSpanPromise;
 
   return new Response('ok', { status: 200 });
 }
diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/pages/api/endpoint-behind-faulty-middleware.ts b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/pages/api/endpoint-behind-faulty-middleware.ts
new file mode 100644
index 000000000000..2ca75a33ba7e
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/pages/api/endpoint-behind-faulty-middleware.ts
@@ -0,0 +1,9 @@
+import type { NextApiRequest, NextApiResponse } from 'next';
+
+type Data = {
+  name: string;
+};
+
+export default function handler(req: NextApiRequest, res: NextApiResponse) {
+  res.status(200).json({ name: 'John Doe' });
+}
diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/async-context-edge.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/async-context-edge.test.ts
index ecce719f0656..cb92cb2bab49 100644
--- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/async-context-edge.test.ts
+++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/async-context-edge.test.ts
@@ -3,7 +3,10 @@ import { waitForTransaction } from '@sentry-internal/test-utils';
 
 test('Should allow for async context isolation in the edge SDK', async ({ request }) => {
   const edgerouteTransactionPromise = waitForTransaction('nextjs-app-dir', async transactionEvent => {
-    return transactionEvent?.transaction === 'GET /api/async-context-edge-endpoint';
+    return (
+      transactionEvent?.transaction === 'GET /api/async-context-edge-endpoint' &&
+      transactionEvent.contexts?.runtime?.name === 'vercel-edge'
+    );
   });
 
   await request.get('/api/async-context-edge-endpoint');
@@ -13,8 +16,5 @@ test('Should allow for async context isolation in the edge SDK', async ({ reques
   const outerSpan = asyncContextEdgerouteTransaction.spans?.find(span => span.description === 'outer-span');
   const innerSpan = asyncContextEdgerouteTransaction.spans?.find(span => span.description === 'inner-span');
 
-  // @ts-expect-error parent_span_id exists
-  expect(outerSpan?.parent_span_id).toStrictEqual(asyncContextEdgerouteTransaction.contexts?.trace?.span_id);
-  // @ts-expect-error parent_span_id exists
-  expect(innerSpan?.parent_span_id).toStrictEqual(asyncContextEdgerouteTransaction.contexts?.trace?.span_id);
+  expect(outerSpan?.parent_span_id).toStrictEqual(innerSpan?.parent_span_id);
 });
diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/edge-route.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/edge-route.test.ts
index 810e76eaa690..6233688317e4 100644
--- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/edge-route.test.ts
+++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/edge-route.test.ts
@@ -5,7 +5,6 @@ test('Should create a transaction for edge routes', async ({ request }) => {
   const edgerouteTransactionPromise = waitForTransaction('nextjs-app-dir', async transactionEvent => {
     return (
       transactionEvent?.transaction === 'GET /api/edge-endpoint' &&
-      transactionEvent?.contexts?.trace?.status === 'ok' &&
       transactionEvent.contexts?.runtime?.name === 'vercel-edge'
     );
   });
@@ -24,31 +23,11 @@ test('Should create a transaction for edge routes', async ({ request }) => {
   expect(edgerouteTransaction.request?.headers?.['x-yeet']).toBe('test-value');
 });
 
-test('Should create a transaction with error status for faulty edge routes', async ({ request }) => {
+test('Faulty edge routes', async ({ request }) => {
   const edgerouteTransactionPromise = waitForTransaction('nextjs-app-dir', async transactionEvent => {
-    return (
-      transactionEvent?.transaction === 'GET /api/error-edge-endpoint' &&
-      transactionEvent?.contexts?.trace?.status === 'unknown_error'
-    );
+    return transactionEvent?.transaction === 'GET /api/error-edge-endpoint';
   });
 
-  request.get('/api/error-edge-endpoint').catch(() => {
-    // Noop
-  });
-
-  const edgerouteTransaction = await edgerouteTransactionPromise;
-
-  expect(edgerouteTransaction.contexts?.trace?.status).toBe('unknown_error');
-  expect(edgerouteTransaction.contexts?.trace?.op).toBe('http.server');
-  expect(edgerouteTransaction.contexts?.runtime?.name).toBe('vercel-edge');
-
-  // Assert that isolation scope works properly
-  expect(edgerouteTransaction.tags?.['my-isolated-tag']).toBe(true);
-  expect(edgerouteTransaction.tags?.['my-global-scope-isolated-tag']).not.toBeDefined();
-});
-
-// TODO(lforst): This cannot make it into production - Make sure to fix this test
-test.skip('Should record exceptions for faulty edge routes', async ({ request }) => {
   const errorEventPromise = waitForError('nextjs-app-dir', errorEvent => {
     return errorEvent?.exception?.values?.[0]?.value === 'Edge Route Error';
   });
@@ -57,11 +36,21 @@ test.skip('Should record exceptions for faulty edge routes', async ({ request })
     // Noop
   });
 
-  const errorEvent = await errorEventPromise;
+  const [edgerouteTransaction, errorEvent] = await Promise.all([
+    test.step('should create a transaction', () => edgerouteTransactionPromise),
+    test.step('should create an error event', () => errorEventPromise),
+  ]);
 
-  // Assert that isolation scope works properly
-  expect(errorEvent.tags?.['my-isolated-tag']).toBe(true);
-  expect(errorEvent.tags?.['my-global-scope-isolated-tag']).not.toBeDefined();
+  test.step('should create transactions with the right fields', () => {
+    expect(edgerouteTransaction.contexts?.trace?.status).toBe('unknown_error');
+    expect(edgerouteTransaction.contexts?.trace?.op).toBe('http.server');
+    expect(edgerouteTransaction.contexts?.runtime?.name).toBe('vercel-edge');
+  });
 
-  expect(errorEvent.transaction).toBe('GET /api/error-edge-endpoint');
+  test.step('should have scope isolation', () => {
+    expect(edgerouteTransaction.tags?.['my-isolated-tag']).toBe(true);
+    expect(edgerouteTransaction.tags?.['my-global-scope-isolated-tag']).not.toBeDefined();
+    expect(errorEvent.tags?.['my-isolated-tag']).toBe(true);
+    expect(errorEvent.tags?.['my-global-scope-isolated-tag']).not.toBeDefined();
+  });
 });
diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/edge.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/edge.test.ts
index de4e2f45ed37..a34d415ee4bf 100644
--- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/edge.test.ts
+++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/edge.test.ts
@@ -19,17 +19,19 @@ test('Should record exceptions for faulty edge server components', async ({ page
   expect(errorEvent.transaction).toBe(`Page Server Component (/edge-server-components/error)`);
 });
 
-// TODO(lforst): This test skip cannot make it into production - make sure to fix this test before merging into develop branch
+// TODO(lforst): Fix this test
 test.skip('Should record transaction for edge server components', async ({ page }) => {
   const serverComponentTransactionPromise = waitForTransaction('nextjs-app-dir', async transactionEvent => {
-    return transactionEvent?.transaction === 'GET /edge-server-components';
+    return (
+      transactionEvent?.transaction === 'GET /edge-server-components' &&
+      transactionEvent.contexts?.runtime?.name === 'vercel-edge'
+    );
   });
 
   await page.goto('/edge-server-components');
 
   const serverComponentTransaction = await serverComponentTransactionPromise;
 
-  expect(serverComponentTransaction).toBe(1);
   expect(serverComponentTransaction).toBeDefined();
   expect(serverComponentTransaction.request?.headers).toBeDefined();
 
diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/middleware.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/middleware.test.ts
index 2fb31bba13a7..5501f9a13b33 100644
--- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/middleware.test.ts
+++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/middleware.test.ts
@@ -3,7 +3,7 @@ import { waitForError, waitForTransaction } from '@sentry-internal/test-utils';
 
 test('Should create a transaction for middleware', async ({ request }) => {
   const middlewareTransactionPromise = waitForTransaction('nextjs-app-dir', async transactionEvent => {
-    return transactionEvent?.transaction === 'middleware' && transactionEvent?.contexts?.trace?.status === 'ok';
+    return transactionEvent?.transaction === 'middleware GET /api/endpoint-behind-middleware';
   });
 
   const response = await request.get('/api/endpoint-behind-middleware');
@@ -12,7 +12,7 @@ test('Should create a transaction for middleware', async ({ request }) => {
   const middlewareTransaction = await middlewareTransactionPromise;
 
   expect(middlewareTransaction.contexts?.trace?.status).toBe('ok');
-  expect(middlewareTransaction.contexts?.trace?.op).toBe('middleware.nextjs');
+  expect(middlewareTransaction.contexts?.trace?.op).toBe('http.server.middleware');
   expect(middlewareTransaction.contexts?.runtime?.name).toBe('vercel-edge');
 
   // Assert that isolation scope works properly
@@ -20,46 +20,40 @@ test('Should create a transaction for middleware', async ({ request }) => {
   expect(middlewareTransaction.tags?.['my-global-scope-isolated-tag']).not.toBeDefined();
 });
 
-test('Should create a transaction with error status for faulty middleware', async ({ request }) => {
+test('Faulty middlewares', async ({ request }) => {
   const middlewareTransactionPromise = waitForTransaction('nextjs-app-dir', async transactionEvent => {
-    return (
-      transactionEvent?.transaction === 'middleware' && transactionEvent?.contexts?.trace?.status === 'unknown_error'
-    );
+    return transactionEvent?.transaction === 'middleware GET /api/endpoint-behind-faulty-middleware';
   });
 
-  request.get('/api/endpoint-behind-middleware', { headers: { 'x-should-throw': '1' } }).catch(() => {
-    // Noop
-  });
-
-  const middlewareTransaction = await middlewareTransactionPromise;
-
-  expect(middlewareTransaction.contexts?.trace?.status).toBe('unknown_error');
-  expect(middlewareTransaction.contexts?.trace?.op).toBe('middleware.nextjs');
-  expect(middlewareTransaction.contexts?.runtime?.name).toBe('vercel-edge');
-});
-
-// TODO(lforst): This cannot make it into production - Make sure to fix this test
-test.skip('Records exceptions happening in middleware', async ({ request }) => {
   const errorEventPromise = waitForError('nextjs-app-dir', errorEvent => {
     return errorEvent?.exception?.values?.[0]?.value === 'Middleware Error';
   });
 
-  request.get('/api/endpoint-behind-middleware', { headers: { 'x-should-throw': '1' } }).catch(() => {
+  request.get('/api/endpoint-behind-faulty-middleware', { headers: { 'x-should-throw': '1' } }).catch(() => {
     // Noop
   });
 
-  const errorEvent = await errorEventPromise;
+  await test.step('should record transactions', async () => {
+    const middlewareTransaction = await middlewareTransactionPromise;
+    expect(middlewareTransaction.contexts?.trace?.status).toBe('unknown_error');
+    expect(middlewareTransaction.contexts?.trace?.op).toBe('http.server.middleware');
+    expect(middlewareTransaction.contexts?.runtime?.name).toBe('vercel-edge');
+  });
 
-  // Assert that isolation scope works properly
-  expect(errorEvent.tags?.['my-isolated-tag']).toBe(true);
-  expect(errorEvent.tags?.['my-global-scope-isolated-tag']).not.toBeDefined();
-  expect(errorEvent.transaction).toBe('middleware');
+  await test.step('should record exceptions', async () => {
+    const errorEvent = await errorEventPromise;
+
+    // Assert that isolation scope works properly
+    expect(errorEvent.tags?.['my-isolated-tag']).toBe(true);
+    expect(errorEvent.tags?.['my-global-scope-isolated-tag']).not.toBeDefined();
+    expect(errorEvent.transaction).toBe('middleware GET /api/endpoint-behind-faulty-middleware');
+  });
 });
 
 test('Should trace outgoing fetch requests inside middleware and create breadcrumbs for it', async ({ request }) => {
   const middlewareTransactionPromise = waitForTransaction('nextjs-app-dir', async transactionEvent => {
     return (
-      transactionEvent?.transaction === 'middleware' &&
+      transactionEvent?.transaction === 'middleware GET /api/endpoint-behind-middleware' &&
       !!transactionEvent.spans?.find(span => span.op === 'http.client')
     );
   });
diff --git a/packages/nextjs/src/common/utils/edgeWrapperUtils.ts b/packages/nextjs/src/common/utils/edgeWrapperUtils.ts
deleted file mode 100644
index 5eed59aca0a3..000000000000
--- a/packages/nextjs/src/common/utils/edgeWrapperUtils.ts
+++ /dev/null
@@ -1,91 +0,0 @@
-import {
-  SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
-  SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
-  SPAN_STATUS_OK,
-  captureException,
-  continueTrace,
-  handleCallbackErrors,
-  setHttpStatus,
-  startSpan,
-  withIsolationScope,
-} from '@sentry/core';
-import { vercelWaitUntil, winterCGRequestToRequestData } from '@sentry/utils';
-
-import type { EdgeRouteHandler } from '../../edge/types';
-import { flushSafelyWithTimeout } from './responseEnd';
-import { commonObjectToIsolationScope, escapeNextjsTracing } from './tracingUtils';
-
-/**
- * Wraps a function on the edge runtime with error and performance monitoring.
- */
-export function withEdgeWrapping(
-  handler: H,
-  options: { spanDescription: string; spanOp: string; mechanismFunctionName: string },
-): (...params: Parameters) => Promise> {
-  return async function (this: unknown, ...args) {
-    return escapeNextjsTracing(() => {
-      const req: unknown = args[0];
-      return withIsolationScope(commonObjectToIsolationScope(req), isolationScope => {
-        let sentryTrace;
-        let baggage;
-
-        if (req instanceof Request) {
-          sentryTrace = req.headers.get('sentry-trace') || '';
-          baggage = req.headers.get('baggage');
-
-          isolationScope.setSDKProcessingMetadata({
-            request: winterCGRequestToRequestData(req),
-          });
-        }
-
-        isolationScope.setTransactionName(options.spanDescription);
-
-        return continueTrace(
-          {
-            sentryTrace,
-            baggage,
-          },
-          () => {
-            return startSpan(
-              {
-                name: options.spanDescription,
-                op: options.spanOp,
-                forceTransaction: true,
-                attributes: {
-                  [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',
-                  [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.nextjs.withEdgeWrapping',
-                },
-              },
-              async span => {
-                const handlerResult = await handleCallbackErrors(
-                  () => handler.apply(this, args),
-                  error => {
-                    captureException(error, {
-                      mechanism: {
-                        type: 'instrument',
-                        handled: false,
-                        data: {
-                          function: options.mechanismFunctionName,
-                        },
-                      },
-                    });
-                  },
-                );
-
-                if (handlerResult instanceof Response) {
-                  setHttpStatus(span, handlerResult.status);
-                } else {
-                  span.setStatus({ code: SPAN_STATUS_OK });
-                }
-
-                return handlerResult;
-              },
-            );
-          },
-        ).finally(() => {
-          vercelWaitUntil(flushSafelyWithTimeout());
-        });
-      });
-    });
-  };
-}
diff --git a/packages/nextjs/src/common/wrapMiddlewareWithSentry.ts b/packages/nextjs/src/common/wrapMiddlewareWithSentry.ts
index 66cbbb046300..9f0903e86984 100644
--- a/packages/nextjs/src/common/wrapMiddlewareWithSentry.ts
+++ b/packages/nextjs/src/common/wrapMiddlewareWithSentry.ts
@@ -1,5 +1,19 @@
+import {
+  SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
+  SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
+  captureException,
+  getActiveSpan,
+  getCurrentScope,
+  getRootSpan,
+  handleCallbackErrors,
+  setCapturedScopesOnSpan,
+  startSpan,
+  withIsolationScope,
+} from '@sentry/core';
+import type { TransactionSource } from '@sentry/types';
+import { vercelWaitUntil, winterCGRequestToRequestData } from '@sentry/utils';
 import type { EdgeRouteHandler } from '../edge/types';
-import { withEdgeWrapping } from './utils/edgeWrapperUtils';
+import { flushSafelyWithTimeout } from './utils/responseEnd';
 
 /**
  * Wraps Next.js middleware with Sentry error and performance instrumentation.
@@ -11,12 +25,69 @@ export function wrapMiddlewareWithSentry(
   middleware: H,
 ): (...params: Parameters) => Promise> {
   return new Proxy(middleware, {
-    apply: (wrappingTarget, thisArg, args: Parameters) => {
-      return withEdgeWrapping(wrappingTarget, {
-        spanDescription: 'middleware',
-        spanOp: 'middleware.nextjs',
-        mechanismFunctionName: 'withSentryMiddleware',
-      }).apply(thisArg, args);
+    apply: async (wrappingTarget, thisArg, args: Parameters) => {
+      // TODO: We still should add central isolation scope creation for when our build-time instrumentation does not work anymore with turbopack.
+      return withIsolationScope(isolationScope => {
+        const req: unknown = args[0];
+        const currentScope = getCurrentScope();
+
+        let spanName: string;
+        let spanOrigin: TransactionSource;
+
+        if (req instanceof Request) {
+          isolationScope.setSDKProcessingMetadata({
+            request: winterCGRequestToRequestData(req),
+          });
+          spanName = `middleware ${req.method} ${new URL(req.url).pathname}`;
+          spanOrigin = 'url';
+        } else {
+          spanName = 'middleware';
+          spanOrigin = 'component';
+        }
+
+        currentScope.setTransactionName(spanName);
+
+        const activeSpan = getActiveSpan();
+
+        if (activeSpan) {
+          // If there is an active span, it likely means that the automatic Next.js OTEL instrumentation worked and we can
+          // rely on that for parameterization.
+          spanName = 'middleware';
+          spanOrigin = 'component';
+
+          const rootSpan = getRootSpan(activeSpan);
+          if (rootSpan) {
+            setCapturedScopesOnSpan(rootSpan, currentScope, isolationScope);
+          }
+        }
+
+        return startSpan(
+          {
+            name: spanName,
+            op: 'http.server.middleware',
+            attributes: {
+              [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: spanOrigin,
+              [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.nextjs.wrapMiddlewareWithSentry',
+            },
+          },
+          () => {
+            return handleCallbackErrors(
+              () => wrappingTarget.apply(thisArg, args),
+              error => {
+                captureException(error, {
+                  mechanism: {
+                    type: 'instrument',
+                    handled: false,
+                  },
+                });
+              },
+              () => {
+                vercelWaitUntil(flushSafelyWithTimeout());
+              },
+            );
+          },
+        );
+      });
     },
   });
 }
diff --git a/packages/nextjs/src/edge/index.ts b/packages/nextjs/src/edge/index.ts
index cf0e571e67cb..0a63118ada46 100644
--- a/packages/nextjs/src/edge/index.ts
+++ b/packages/nextjs/src/edge/index.ts
@@ -1,20 +1,17 @@
-import { context } from '@opentelemetry/api';
 import {
+  SEMANTIC_ATTRIBUTE_SENTRY_OP,
   applySdkMetadata,
-  getCapturedScopesOnSpan,
-  getCurrentScope,
-  getIsolationScope,
   getRootSpan,
   registerSpanErrorInstrumentation,
-  setCapturedScopesOnSpan,
+  spanToJSON,
 } from '@sentry/core';
 
-import { GLOBAL_OBJ } from '@sentry/utils';
+import { GLOBAL_OBJ, vercelWaitUntil } from '@sentry/utils';
 import type { VercelEdgeOptions } from '@sentry/vercel-edge';
 import { getDefaultIntegrations, init as vercelEdgeInit } from '@sentry/vercel-edge';
 
-import { getScopesFromContext } from '@sentry/opentelemetry';
 import { isBuild } from '../common/utils/isBuild';
+import { flushSafelyWithTimeout } from '../common/utils/responseEnd';
 import { distDirRewriteFramesIntegration } from './distDirRewriteFramesIntegration';
 
 export { captureUnderscoreErrorException } from '../common/pages-router-instrumentation/_error';
@@ -52,20 +49,18 @@ export function init(options: VercelEdgeOptions = {}): void {
 
   const client = vercelEdgeInit(opts);
 
-  // Create/fork an isolation whenever we create root spans. This is ok because in Next.js we only create root spans on the edge for incoming requests.
   client?.on('spanStart', span => {
-    if (span === getRootSpan(span)) {
-      const scopes = getCapturedScopesOnSpan(span);
-
-      const isolationScope = (scopes.isolationScope || getIsolationScope()).clone();
-      const scope = scopes.scope || getCurrentScope();
+    const spanAttributes = spanToJSON(span).data;
 
-      const currentScopesPointer = getScopesFromContext(context.active());
-      if (currentScopesPointer) {
-        currentScopesPointer.isolationScope = isolationScope;
-      }
+    // Make sure middleware spans get the right op
+    if (spanAttributes?.['next.span_type'] === 'Middleware.execute') {
+      span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'http.server.middleware');
+    }
+  });
 
-      setCapturedScopesOnSpan(span, scope, isolationScope);
+  client?.on('spanEnd', span => {
+    if (span === getRootSpan(span)) {
+      vercelWaitUntil(flushSafelyWithTimeout());
     }
   });
 }
diff --git a/packages/nextjs/src/edge/wrapApiHandlerWithSentry.ts b/packages/nextjs/src/edge/wrapApiHandlerWithSentry.ts
index e5191ea27dbe..5c8ce043ecb8 100644
--- a/packages/nextjs/src/edge/wrapApiHandlerWithSentry.ts
+++ b/packages/nextjs/src/edge/wrapApiHandlerWithSentry.ts
@@ -1,4 +1,18 @@
-import { withEdgeWrapping } from '../common/utils/edgeWrapperUtils';
+import {
+  SEMANTIC_ATTRIBUTE_SENTRY_OP,
+  SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
+  SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
+  captureException,
+  getActiveSpan,
+  getCurrentScope,
+  getRootSpan,
+  handleCallbackErrors,
+  setCapturedScopesOnSpan,
+  startSpan,
+  withIsolationScope,
+} from '@sentry/core';
+import { vercelWaitUntil, winterCGRequestToRequestData } from '@sentry/utils';
+import { flushSafelyWithTimeout } from '../common/utils/responseEnd';
 import type { EdgeRouteHandler } from './types';
 
 /**
@@ -9,18 +23,76 @@ export function wrapApiHandlerWithSentry(
   parameterizedRoute: string,
 ): (...params: Parameters) => Promise> {
   return new Proxy(handler, {
-    apply: (wrappingTarget, thisArg, args: Parameters) => {
-      const req = args[0];
+    apply: async (wrappingTarget, thisArg, args: Parameters) => {
+      // TODO: We still should add central isolation scope creation for when our build-time instrumentation does not work anymore with turbopack.
 
-      const wrappedHandler = withEdgeWrapping(wrappingTarget, {
-        spanDescription: !(req instanceof Request)
-          ? `handler (${parameterizedRoute})`
-          : `${req.method} ${parameterizedRoute}`,
-        spanOp: 'http.server',
-        mechanismFunctionName: 'wrapApiHandlerWithSentry',
-      });
+      return withIsolationScope(isolationScope => {
+        const req: unknown = args[0];
+        const currentScope = getCurrentScope();
+
+        if (req instanceof Request) {
+          isolationScope.setSDKProcessingMetadata({
+            request: winterCGRequestToRequestData(req),
+          });
+          currentScope.setTransactionName(`${req.method} ${parameterizedRoute}`);
+        } else {
+          currentScope.setTransactionName(`handler (${parameterizedRoute})`);
+        }
+
+        let spanName: string;
+        let op: string | undefined = 'http.server';
 
-      return wrappedHandler.apply(thisArg, args);
+        // If there is an active span, it likely means that the automatic Next.js OTEL instrumentation worked and we can
+        // rely on that for parameterization.
+        const activeSpan = getActiveSpan();
+        if (activeSpan) {
+          spanName = `handler (${parameterizedRoute})`;
+          op = undefined;
+
+          const rootSpan = getRootSpan(activeSpan);
+          if (rootSpan) {
+            rootSpan.updateName(
+              req instanceof Request ? `${req.method} ${parameterizedRoute}` : `handler ${parameterizedRoute}`,
+            );
+            rootSpan.setAttributes({
+              [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'http.server',
+              [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',
+            });
+            setCapturedScopesOnSpan(rootSpan, currentScope, isolationScope);
+          }
+        } else if (req instanceof Request) {
+          spanName = `${req.method} ${parameterizedRoute}`;
+        } else {
+          spanName = `handler ${parameterizedRoute}`;
+        }
+
+        return startSpan(
+          {
+            name: spanName,
+            op: op,
+            attributes: {
+              [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',
+              [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.nextjs.wrapApiHandlerWithSentry',
+            },
+          },
+          () => {
+            return handleCallbackErrors(
+              () => wrappingTarget.apply(thisArg, args),
+              error => {
+                captureException(error, {
+                  mechanism: {
+                    type: 'instrument',
+                    handled: false,
+                  },
+                });
+              },
+              () => {
+                vercelWaitUntil(flushSafelyWithTimeout());
+              },
+            );
+          },
+        );
+      });
     },
   });
 }
diff --git a/packages/nextjs/test/edge/edgeWrapperUtils.test.ts b/packages/nextjs/test/edge/edgeWrapperUtils.test.ts
deleted file mode 100644
index 029ee9d97fce..000000000000
--- a/packages/nextjs/test/edge/edgeWrapperUtils.test.ts
+++ /dev/null
@@ -1,109 +0,0 @@
-import * as coreSdk from '@sentry/core';
-import { SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from '@sentry/core';
-
-import { withEdgeWrapping } from '../../src/common/utils/edgeWrapperUtils';
-
-const origRequest = global.Request;
-const origResponse = global.Response;
-
-// @ts-expect-error Request does not exist on type Global
-global.Request = class Request {
-  headers = {
-    get() {
-      return null;
-    },
-  };
-};
-
-// @ts-expect-error Response does not exist on type Global
-global.Response = class Request {};
-
-afterAll(() => {
-  global.Request = origRequest;
-  global.Response = origResponse;
-});
-
-beforeEach(() => {
-  jest.clearAllMocks();
-});
-
-describe('withEdgeWrapping', () => {
-  it('should return a function that calls the passed function', async () => {
-    const origFunctionReturnValue = new Response();
-    const origFunction = jest.fn(_req => origFunctionReturnValue);
-
-    const wrappedFunction = withEdgeWrapping(origFunction, {
-      spanDescription: 'some label',
-      mechanismFunctionName: 'some name',
-      spanOp: 'some op',
-    });
-
-    const returnValue = await wrappedFunction(new Request('https://sentry.io/'));
-
-    expect(returnValue).toBe(origFunctionReturnValue);
-    expect(origFunction).toHaveBeenCalledTimes(1);
-  });
-
-  it('should return a function that calls captureException on error', async () => {
-    const captureExceptionSpy = jest.spyOn(coreSdk, 'captureException');
-    const error = new Error();
-    const origFunction = jest.fn(_req => {
-      throw error;
-    });
-
-    const wrappedFunction = withEdgeWrapping(origFunction, {
-      spanDescription: 'some label',
-      mechanismFunctionName: 'some name',
-      spanOp: 'some op',
-    });
-
-    await expect(wrappedFunction(new Request('https://sentry.io/'))).rejects.toBe(error);
-    expect(captureExceptionSpy).toHaveBeenCalledTimes(1);
-  });
-
-  it('should return a function that calls trace', async () => {
-    const startSpanSpy = jest.spyOn(coreSdk, 'startSpan');
-
-    const request = new Request('https://sentry.io/');
-    const origFunction = jest.fn(_req => new Response());
-
-    const wrappedFunction = withEdgeWrapping(origFunction, {
-      spanDescription: 'some label',
-      mechanismFunctionName: 'some name',
-      spanOp: 'some op',
-    });
-
-    await wrappedFunction(request);
-
-    expect(startSpanSpy).toHaveBeenCalledTimes(1);
-    expect(startSpanSpy).toHaveBeenCalledWith(
-      expect.objectContaining({
-        attributes: {
-          [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',
-          [coreSdk.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.nextjs.withEdgeWrapping',
-        },
-        name: 'some label',
-        op: 'some op',
-      }),
-      expect.any(Function),
-    );
-
-    expect(coreSdk.getIsolationScope().getScopeData().sdkProcessingMetadata).toEqual({
-      request: { headers: {} },
-    });
-  });
-
-  it("should return a function that doesn't crash when req isn't passed", async () => {
-    const origFunctionReturnValue = new Response();
-    const origFunction = jest.fn(() => origFunctionReturnValue);
-
-    const wrappedFunction = withEdgeWrapping(origFunction, {
-      spanDescription: 'some label',
-      mechanismFunctionName: 'some name',
-      spanOp: 'some op',
-    });
-
-    await expect(wrappedFunction()).resolves.toBe(origFunctionReturnValue);
-    expect(origFunction).toHaveBeenCalledTimes(1);
-  });
-});
diff --git a/packages/nextjs/test/edge/withSentryAPI.test.ts b/packages/nextjs/test/edge/withSentryAPI.test.ts
index 6e24eca21bfe..11449da0e1ef 100644
--- a/packages/nextjs/test/edge/withSentryAPI.test.ts
+++ b/packages/nextjs/test/edge/withSentryAPI.test.ts
@@ -1,6 +1,3 @@
-import * as coreSdk from '@sentry/core';
-import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from '@sentry/core';
-
 import { wrapApiHandlerWithSentry } from '../../src/edge';
 
 const origRequest = global.Request;
@@ -31,53 +28,16 @@ afterAll(() => {
   global.Response = origResponse;
 });
 
-const startSpanSpy = jest.spyOn(coreSdk, 'startSpan');
-
 afterEach(() => {
   jest.clearAllMocks();
 });
 
 describe('wrapApiHandlerWithSentry', () => {
-  it('should return a function that calls trace', async () => {
-    const request = new Request('https://sentry.io/');
-    const origFunction = jest.fn(_req => new Response());
-
-    const wrappedFunction = wrapApiHandlerWithSentry(origFunction, '/user/[userId]/post/[postId]');
-
-    await wrappedFunction(request);
-
-    expect(startSpanSpy).toHaveBeenCalledTimes(1);
-    expect(startSpanSpy).toHaveBeenCalledWith(
-      expect.objectContaining({
-        attributes: {
-          [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',
-          [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.nextjs.withEdgeWrapping',
-        },
-        name: 'POST /user/[userId]/post/[postId]',
-        op: 'http.server',
-      }),
-      expect.any(Function),
-    );
-  });
-
-  it('should return a function that calls trace without throwing when no request is passed', async () => {
+  it('should return a function that does not throw when no request is passed', async () => {
     const origFunction = jest.fn(() => new Response());
 
     const wrappedFunction = wrapApiHandlerWithSentry(origFunction, '/user/[userId]/post/[postId]');
 
     await wrappedFunction();
-
-    expect(startSpanSpy).toHaveBeenCalledTimes(1);
-    expect(startSpanSpy).toHaveBeenCalledWith(
-      expect.objectContaining({
-        attributes: {
-          [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',
-          [coreSdk.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.nextjs.withEdgeWrapping',
-        },
-        name: 'handler (/user/[userId]/post/[postId])',
-        op: 'http.server',
-      }),
-      expect.any(Function),
-    );
   });
 });