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 5501f9a13b33..a00a29672ed6 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
@@ -14,6 +14,7 @@ test('Should create a transaction for middleware', async ({ request }) => {
   expect(middlewareTransaction.contexts?.trace?.status).toBe('ok');
   expect(middlewareTransaction.contexts?.trace?.op).toBe('http.server.middleware');
   expect(middlewareTransaction.contexts?.runtime?.name).toBe('vercel-edge');
+  expect(middlewareTransaction.transaction_info?.source).toBe('url');
 
   // Assert that isolation scope works properly
   expect(middlewareTransaction.tags?.['my-isolated-tag']).toBe(true);
@@ -38,6 +39,7 @@ test('Faulty middlewares', async ({ request }) => {
     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');
+    expect(middlewareTransaction.transaction_info?.source).toBe('url');
   });
 
   await test.step('should record exceptions', async () => {
diff --git a/packages/nextjs/src/common/wrapMiddlewareWithSentry.ts b/packages/nextjs/src/common/wrapMiddlewareWithSentry.ts
index 9f0903e86984..e8b57c7d2b8b 100644
--- a/packages/nextjs/src/common/wrapMiddlewareWithSentry.ts
+++ b/packages/nextjs/src/common/wrapMiddlewareWithSentry.ts
@@ -32,17 +32,17 @@ export function wrapMiddlewareWithSentry<H extends EdgeRouteHandler>(
         const currentScope = getCurrentScope();
 
         let spanName: string;
-        let spanOrigin: TransactionSource;
+        let spanSource: TransactionSource;
 
         if (req instanceof Request) {
           isolationScope.setSDKProcessingMetadata({
             request: winterCGRequestToRequestData(req),
           });
           spanName = `middleware ${req.method} ${new URL(req.url).pathname}`;
-          spanOrigin = 'url';
+          spanSource = 'url';
         } else {
           spanName = 'middleware';
-          spanOrigin = 'component';
+          spanSource = 'component';
         }
 
         currentScope.setTransactionName(spanName);
@@ -53,7 +53,7 @@ export function wrapMiddlewareWithSentry<H extends EdgeRouteHandler>(
           // 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';
+          spanSource = 'component';
 
           const rootSpan = getRootSpan(activeSpan);
           if (rootSpan) {
@@ -66,7 +66,7 @@ export function wrapMiddlewareWithSentry<H extends EdgeRouteHandler>(
             name: spanName,
             op: 'http.server.middleware',
             attributes: {
-              [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: spanOrigin,
+              [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: spanSource,
               [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.nextjs.wrapMiddlewareWithSentry',
             },
           },
diff --git a/packages/nextjs/src/edge/index.ts b/packages/nextjs/src/edge/index.ts
index 0a63118ada46..fff4236bf3be 100644
--- a/packages/nextjs/src/edge/index.ts
+++ b/packages/nextjs/src/edge/index.ts
@@ -1,12 +1,14 @@
 import {
   SEMANTIC_ATTRIBUTE_SENTRY_OP,
+  SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
+  SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
   applySdkMetadata,
   getRootSpan,
   registerSpanErrorInstrumentation,
   spanToJSON,
 } from '@sentry/core';
 
-import { GLOBAL_OBJ, vercelWaitUntil } from '@sentry/utils';
+import { GLOBAL_OBJ, stripUrlQueryAndFragment, vercelWaitUntil } from '@sentry/utils';
 import type { VercelEdgeOptions } from '@sentry/vercel-edge';
 import { getDefaultIntegrations, init as vercelEdgeInit } from '@sentry/vercel-edge';
 
@@ -52,9 +54,31 @@ export function init(options: VercelEdgeOptions = {}): void {
   client?.on('spanStart', span => {
     const spanAttributes = spanToJSON(span).data;
 
+    // Mark all spans generated by Next.js as 'auto'
+    if (spanAttributes?.['next.span_type'] !== undefined) {
+      span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, 'auto');
+    }
+
     // Make sure middleware spans get the right op
     if (spanAttributes?.['next.span_type'] === 'Middleware.execute') {
       span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'http.server.middleware');
+      span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'url');
+    }
+  });
+
+  // Use the preprocessEvent hook instead of an event processor, so that the users event processors receive the most
+  // up-to-date value, but also so that the logic that detects changes to the transaction names to set the source to
+  // "custom", doesn't trigger.
+  client?.on('preprocessEvent', event => {
+    // The otel auto inference will clobber the transaction name because the span has an http.target
+    if (
+      event.type === 'transaction' &&
+      event.contexts?.trace?.data?.['next.span_type'] === 'Middleware.execute' &&
+      event.contexts?.trace?.data?.['next.span_name']
+    ) {
+      if (event.transaction) {
+        event.transaction = stripUrlQueryAndFragment(event.contexts.trace.data['next.span_name']);
+      }
     }
   });