diff --git src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php
index a90308101a..31cb070b94 100644
--- src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php
+++ src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php
@@ -237,9 +237,6 @@ class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller {
 
 		$attachment = get_post( $request['id'] );
 
-		/** This action is documented in wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php */
-		do_action( 'rest_insert_attachment', $data, $request, false );
-
 		$fields_update = $this->update_additional_fields_for_object( $attachment, $request );
 
 		if ( is_wp_error( $fields_update ) ) {
diff --git src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php
index c2b707dfa0..d008dbfa0b 100644
--- src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php
+++ src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php
@@ -739,11 +739,15 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
 
 		$request->set_param( 'context', 'edit' );
 
+		$response = $this->prepare_item_for_response( $post, $request );
+
+		if ( 'attachment' === $this->post_type ) {
+			return rest_ensure_response( $response );
+		}
+
 		/** This action is documented in wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php */
 		do_action( "rest_after_insert_{$this->post_type}", $post, $request, false );
 
-		$response = $this->prepare_item_for_response( $post, $request );
-
 		return rest_ensure_response( $response );
 	}
 
diff --git tests/phpunit/tests/rest-api/rest-attachments-controller.php tests/phpunit/tests/rest-api/rest-attachments-controller.php
index fa22a4a39d..ce9d15f0ee 100644
--- tests/phpunit/tests/rest-api/rest-attachments-controller.php
+++ tests/phpunit/tests/rest-api/rest-attachments-controller.php
@@ -16,6 +16,8 @@ class WP_Test_REST_Attachments_Controller extends WP_Test_REST_Post_Type_Control
 	protected static $author_id;
 	protected static $contributor_id;
 	protected static $uploader_id;
+	protected static $rest_after_insert_attachment_count;
+	protected static $rest_insert_attachment_count;
 
 	public static function wpSetUpBeforeClass( $factory ) {
 		self::$superadmin_id = $factory->user->create( array(
@@ -1437,4 +1439,37 @@ class WP_Test_REST_Attachments_Controller extends WP_Test_REST_Post_Type_Control
 		$this->assertErrorResponse( 'rest_upload_limited_space', $response, 400 );
 	}
 
+	/**
+	 * Ensure the `rest_after_insert_attachment` and `rest_insert_attachment` hooks only fire
+	 * once when attachments are updated.
+	 *
+	 * @ticket 45269
+	 */
+	public function test_rest_insert_attachment_hooks_fire_once_on_update() {
+		self::$rest_insert_attachment_count = 0;
+		self::$rest_after_insert_attachment_count = 0;
+		add_action( 'rest_insert_attachment', array( $this, 'filter_rest_insert_attachment' ) );
+		add_action( 'rest_after_insert_attachment', array( $this, 'filter_rest_after_insert_attachment' ) );
+
+		wp_set_current_user( self::$editor_id );
+		$attachment_id = $this->factory->attachment->create_object( $this->test_file, 0, array(
+			'post_mime_type' => 'image/jpeg',
+			'post_excerpt'   => 'A sample caption',
+			'post_author'    => self::$editor_id,
+		) );
+		$request = new WP_REST_Request( 'POST', '/wp/v2/media/' . $attachment_id );
+		$request->set_param( 'title', 'My title is very cool' );
+		$response = $this->server->dispatch( $request );
+
+		$this->assertSame( 1, self::$rest_insert_attachment_count );
+		$this->assertSame( 1, self::$rest_after_insert_attachment_count );
+	}
+
+	public function filter_rest_insert_attachment( $attachment ) {
+		self::$rest_insert_attachment_count++;
+	}
+
+	public function filter_rest_after_insert_attachment( $attachment ) {
+		self::$rest_after_insert_attachment_count++;
+	}
 }
