diff --git a/wp-includes/canonical.php b/wp-includes/canonical.php
index 8b13789..8f52abc 100644
--- a/wp-includes/canonical.php
+++ b/wp-includes/canonical.php
@@ -445,11 +445,37 @@ function redirect_canonical( $requested_url = null, $do_redirect = true ) {
 		$redirect['path'] = $redirect_obj->path;
 
 	// Attachment (non-empty attachment_id, no attachment slug, & no other query vars).
-	} elseif ( is_attachment()
-		&& ! array_diff( array_keys( $wp->query_vars ), array( 'attachment', 'attachment_id' ) )
-		&& ! $redirect_url
-	) {
-		$redirect_url = get_attachment_link( get_query_var( 'attachment_id' ) );
-		$redirect_obj = get_post( get_query_var( 'attachment_id' ) );
+	} elseif ( is_attachment()
+		&& ! array_diff( array_keys( $wp->query_vars ), array( 'attachment', 'attachment_id' ) )
+		&& ! $redirect_url
+	) {
+
+		// Existing WordPress canonical behavior.
+		$redirect_url = get_attachment_link( get_query_var( 'attachment_id' ) );
+		$redirect_obj = get_post( get_query_var( 'attachment_id' ) );
+
+		/**
+		 * NEW: Redirect old media URLs to the new plain-name permalink structure.
+		 *
+		 * OLD:
+		 *   /?attachment_id=123
+		 *   /uploads/2023/10/photo.jpg
+		 *
+		 * NEW:
+		 *   /photo/
+		 */
+		if ( $redirect_obj && 'attachment' === $redirect_obj->post_type ) {
+
+			// Build clean permalink based on post_name.
+			$new_structure_url = home_url( '/' . $redirect_obj->post_name . '/' );
+
+			// Apply redirect only if URL differs from current permalink.
+			if ( $new_structure_url !== $redirect_url ) {
+				$redirect_url = $new_structure_url;
+			}
+		}
+
+		if ( $redirect_url ) {
+			$redirect['query'] = remove_query_arg( 'attachment_id', $redirect['query'] );
+		}
 	}
 
 	// Strip off non-trailing slash if not using trailing slashes.
