diff --git src/wp-admin/includes/post.php src/wp-admin/includes/post.php
index ed73b8ff5f..4bc92d1e4c 100644
--- src/wp-admin/includes/post.php
+++ src/wp-admin/includes/post.php
@@ -1961,7 +1961,7 @@ function wp_create_post_autosave( $post_data ) {
 		 * @param array $new_autosave Post array - the autosave that is about to be saved.
 		 */
 		do_action( 'wp_creating_autosave', $new_autosave );
-
+		wp_autosave_post_revisioned_meta_fields( $new_autosave );
 		return wp_update_post( $new_autosave );
 	}
 
@@ -1969,7 +1969,65 @@ function wp_create_post_autosave( $post_data ) {
 	$post_data = wp_unslash( $post_data );
 
 	// Otherwise create the new autosave as a special post revision.
-	return _wp_put_post_revision( $post_data, true );
+	$revision = _wp_put_post_revision( $post_data, true );
+
+	// Update the revisioned meta data as well.
+	wp_autosave_post_revisioned_meta_fields( $revision );
+	return $revision;
+}
+
+/**
+ * Autosave the revisioned meta fields.
+ *
+ * Iterates thru the revisioned meta fields and checks each to see if they are set,
+ * and have a changed value. If so, the meta value is saved and attached to the autosave.
+ *
+ * @since 6.4.0
+ *
+ * @param Post object $new_autosave The new post being autosaved.
+ */
+function wp_autosave_post_revisioned_meta_fields( $new_autosave ) {
+	/*
+	 * The post data arrives as either $_POST['data']['wp_autosave'] or the $_POST
+	 * itself. This sets $posted_data to the correct variable.
+	 *
+	 * Ignoring sanitization to avoid altering meta. Ignoring the nonce check because
+	 * this is hooked on inner core hooks where a valid nonce was already checked.
+	 *
+	 * @phpcs:disable WordPress.Security
+	 */
+	$posted_data = isset( $_POST['data']['wp_autosave'] ) ? $_POST['data']['wp_autosave'] : $_POST;
+	// phpcs:enable
+
+	/*
+	 * Go thru the revisioned meta keys and save them as part of the autosave, if
+	 * the meta key is part of the posted data, the meta value is not blank and
+	 * the the meta value has changes from the last autosaved value.
+	 */
+	foreach ( wp_post_revision_meta_keys() as $meta_key ) {
+
+		if (
+		isset( $posted_data[ $meta_key ] ) &&
+		get_post_meta( $new_autosave['ID'], $meta_key, true ) !== wp_unslash( $posted_data[ $meta_key ] )
+		) {
+			/*
+			 * Use the underlying delete_metadata() and add_metadata() functions
+			 * vs delete_post_meta() and add_post_meta() to make sure we're working
+			 * with the actual revision meta.
+			 */
+			delete_metadata( 'post', $new_autosave['ID'], $meta_key );
+
+			/*
+			 * One last check to ensure meta value not empty().
+			 */
+			if ( ! empty( $posted_data[ $meta_key ] ) ) {
+				/*
+				 * Add the revisions meta data to the autosave.
+				 */
+				add_metadata( 'post', $new_autosave['ID'], $meta_key, $posted_data[ $meta_key ] );
+			}
+		}
+	}
 }
 
 /**
diff --git src/wp-includes/blocks/footnotes.php src/wp-includes/blocks/footnotes.php
index bd7734d7d0..5f57890956 100644
--- src/wp-includes/blocks/footnotes.php
+++ src/wp-includes/blocks/footnotes.php
@@ -68,9 +68,10 @@ function register_block_core_footnotes() {
 			$post_type,
 			'footnotes',
 			array(
-				'show_in_rest' => true,
-				'single'       => true,
-				'type'         => 'string',
+				'show_in_rest'      => true,
+				'single'            => true,
+				'type'              => 'string',
+				'revisions_enabled' => true,
 			)
 		);
 	}
@@ -83,106 +84,6 @@ function register_block_core_footnotes() {
 }
 add_action( 'init', 'register_block_core_footnotes' );
 
-/**
- * Saves the footnotes meta value to the revision.
- *
- * @since 6.3.0
- *
- * @param int $revision_id The revision ID.
- */
-function wp_save_footnotes_meta( $revision_id ) {
-	$post_id = wp_is_post_revision( $revision_id );
-
-	if ( $post_id ) {
-		$footnotes = get_post_meta( $post_id, 'footnotes', true );
-
-		if ( $footnotes ) {
-			// Can't use update_post_meta() because it doesn't allow revisions.
-			update_metadata( 'post', $revision_id, 'footnotes', $footnotes );
-		}
-	}
-}
-add_action( 'wp_after_insert_post', 'wp_save_footnotes_meta' );
-
-/**
- * Keeps track of the revision ID for "rest_after_insert_{$post_type}".
- *
- * @since 6.3.0
- *
- * @global int $wp_temporary_footnote_revision_id The footnote revision ID.
- *
- * @param int $revision_id The revision ID.
- */
-function wp_keep_footnotes_revision_id( $revision_id ) {
-	global $wp_temporary_footnote_revision_id;
-	$wp_temporary_footnote_revision_id = $revision_id;
-}
-add_action( '_wp_put_post_revision', 'wp_keep_footnotes_revision_id' );
-
-/**
- * This is a specific fix for the REST API. The REST API doesn't update
- * the post and post meta in one go (through `meta_input`). While it
- * does fix the `wp_after_insert_post` hook to be called correctly after
- * updating meta, it does NOT fix hooks such as post_updated and
- * save_post, which are normally also fired after post meta is updated
- * in `wp_insert_post()`. Unfortunately, `wp_save_post_revision` is
- * added to the `post_updated` action, which means the meta is not
- * available at the time, so we have to add it afterwards through the
- * `"rest_after_insert_{$post_type}"` action.
- *
- * @since 6.3.0
- *
- * @global int $wp_temporary_footnote_revision_id The footnote revision ID.
- *
- * @param WP_Post $post The post object.
- */
-function wp_add_footnotes_revisions_to_post_meta( $post ) {
-	global $wp_temporary_footnote_revision_id;
-
-	if ( $wp_temporary_footnote_revision_id ) {
-		$revision = get_post( $wp_temporary_footnote_revision_id );
-
-		if ( ! $revision ) {
-			return;
-		}
-
-		$post_id = $revision->post_parent;
-
-		// Just making sure we're updating the right revision.
-		if ( $post->ID === $post_id ) {
-			$footnotes = get_post_meta( $post_id, 'footnotes', true );
-
-			if ( $footnotes ) {
-				// Can't use update_post_meta() because it doesn't allow revisions.
-				update_metadata( 'post', $wp_temporary_footnote_revision_id, 'footnotes', $footnotes );
-			}
-		}
-	}
-}
-
-foreach ( array( 'post', 'page' ) as $post_type ) {
-	add_action( "rest_after_insert_{$post_type}", 'wp_add_footnotes_revisions_to_post_meta' );
-}
-
-/**
- * Restores the footnotes meta value from the revision.
- *
- * @since 6.3.0
- *
- * @param int $post_id     The post ID.
- * @param int $revision_id The revision ID.
- */
-function wp_restore_footnotes_from_revision( $post_id, $revision_id ) {
-	$footnotes = get_post_meta( $revision_id, 'footnotes', true );
-
-	if ( $footnotes ) {
-		update_post_meta( $post_id, 'footnotes', $footnotes );
-	} else {
-		delete_post_meta( $post_id, 'footnotes' );
-	}
-}
-add_action( 'wp_restore_post_revision', 'wp_restore_footnotes_from_revision', 10, 2 );
-
 /**
  * Adds the footnotes field to the revision.
  *
@@ -209,7 +110,20 @@ add_filter( '_wp_post_revision_fields', 'wp_add_footnotes_to_revision' );
  * @return string The field value.
  */
 function wp_get_footnotes_from_revision( $revision_field, $field, $revision ) {
-	return get_metadata( 'post', $revision->ID, $field, true );
+	$footnotes = json_decode( get_metadata( 'post', $revision->ID, $field, true ) );
+	if ( empty( $footnotes ) ) {
+		return $revision_field;
+	}
+	$footnotes_html = '';
+	$x = 1;
+	foreach( $footnotes as $footnote ) {
+		$footnotes_html .= sprintf(
+			"%s.  %s\n",
+			$x++,
+			$footnote->content
+		);
+	}
+	return $footnotes_html;
 }
 add_filter( '_wp_post_revision_field_footnotes', 'wp_get_footnotes_from_revision', 10, 3 );
 
diff --git src/wp-includes/default-filters.php src/wp-includes/default-filters.php
index bf39f29b4d..666b30368b 100644
--- src/wp-includes/default-filters.php
+++ src/wp-includes/default-filters.php
@@ -719,4 +719,10 @@ add_action( 'init', 'wp_register_persisted_preferences_meta' );
 // CPT wp_block custom postmeta field.
 add_action( 'init', 'wp_create_initial_post_meta' );
 
+// Including revisioned meta when considering whether a post revision has changed.
+add_filter( 'wp_save_post_revision_post_has_changed', 'wp_check_revisioned_meta_fields_have_changed', 10, 3 );
+
+// Save revisioned post meta immediately after a revision is saved
+add_action( '_wp_put_post_revision', 'wp_save_revisioned_meta_fields', 10, 2 );
+
 unset( $filter, $action );
diff --git src/wp-includes/meta.php src/wp-includes/meta.php
index 0b9f08544e..f4ea49aa4d 100644
--- src/wp-includes/meta.php
+++ src/wp-includes/meta.php
@@ -1367,6 +1367,7 @@ function sanitize_meta( $meta_key, $meta_value, $object_type, $object_subtype =
  * @since 4.9.8 The `$object_subtype` argument was added to the arguments array.
  * @since 5.3.0 Valid meta types expanded to include "array" and "object".
  * @since 5.5.0 The `$default` argument was added to the arguments array.
+ * @since 6.4.0 The `$revisions_enabled` argument was added to the arguments array.
  *
  * @param string       $object_type Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user',
  *                                  or any other object type with an associated meta table.
@@ -1392,6 +1393,7 @@ function sanitize_meta( $meta_key, $meta_value, $object_type, $object_subtype =
  *                                         support for custom fields for registered meta to be accessible via REST.
  *                                         When registering complex meta values this argument may optionally be an
  *                                         array with 'schema' or 'prepare_callback' keys instead of a boolean.
+ *     @type bool       $revisions_enabled Whether to enable revisions support for this meta_key.
  * }
  * @param string|array $deprecated Deprecated. Use `$args` instead.
  * @return bool True if the meta key was successfully registered in the global array, false if not.
@@ -1400,11 +1402,16 @@ function sanitize_meta( $meta_key, $meta_value, $object_type, $object_subtype =
  */
 function register_meta( $object_type, $meta_key, $args, $deprecated = null ) {
 	global $wp_meta_keys;
+	global $wp_revisioned_meta_keys;
 
 	if ( ! is_array( $wp_meta_keys ) ) {
 		$wp_meta_keys = array();
 	}
 
+	if ( ! is_array( $wp_revisioned_meta_keys ) ) {
+		$wp_revisioned_meta_keys = array();
+	}
+
 	$defaults = array(
 		'object_subtype'    => '',
 		'type'              => 'string',
@@ -1414,6 +1421,7 @@ function register_meta( $object_type, $meta_key, $args, $deprecated = null ) {
 		'sanitize_callback' => null,
 		'auth_callback'     => null,
 		'show_in_rest'      => false,
+		'revisions_enabled' => false,
 	);
 
 	// There used to be individual args for sanitize and auth callbacks.
@@ -1459,6 +1467,11 @@ function register_meta( $object_type, $meta_key, $args, $deprecated = null ) {
 		}
 	}
 
+	// Store the revisioned meta fields.
+	if ( isset( $args['revisions_enabled'] ) && $args['revisions_enabled'] && ! in_array( $meta_key, $wp_revisioned_meta_keys ) ) {
+		array_push( $wp_revisioned_meta_keys, $meta_key );
+	}
+
 	$object_subtype = ! empty( $args['object_subtype'] ) ? $args['object_subtype'] : '';
 
 	// If `auth_callback` is not provided, fall back to `is_protected_meta()`.
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 4ec972347f..86a41b0e0d 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
@@ -673,6 +673,9 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
 			);
 		}
 
+		// Don't save the post meta revision because they aren't stored yet.
+		remove_action( '_wp_put_post_revision', 'wp_save_revisioned_meta_fields', 10, 2 );
+
 		$post_id = wp_insert_post( wp_slash( (array) $prepared_post ), true, false );
 
 		if ( is_wp_error( $post_id ) ) {
@@ -743,6 +746,13 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
 			}
 		}
 
+		// Update any revisioned meta.
+		$revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) );
+		if ( ! empty( $revisions ) ) {
+			$revision  = array_shift( $revisions );
+			wp_save_revisioned_meta_fields( $revision->ID, $post_id );
+		}
+
 		$post          = get_post( $post_id );
 		$fields_update = $this->update_additional_fields_for_object( $post, $request );
 
@@ -876,6 +886,9 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
 			);
 		}
 
+		// Don't save the post meta revision because they aren't stored yet.
+		remove_action( '_wp_put_post_revision', 'wp_save_revisioned_meta_fields', 10, 2 );
+
 		// Convert the post object to an array, otherwise wp_update_post() will expect non-escaped input.
 		$post_id = wp_update_post( wp_slash( (array) $post ), true, false );
 
@@ -928,6 +941,12 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
 				return $meta_update;
 			}
 		}
+		// Update any revisioned meta.
+		$revisions = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) );
+		if ( ! empty( $revisions ) ) {
+			$revision  = array_shift( $revisions );
+			wp_save_revisioned_meta_fields( $revision->ID, $post_id );
+		}
 
 		$post          = get_post( $post_id );
 		$fields_update = $this->update_additional_fields_for_object( $post, $request );
diff --git src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php
index 6fde5f13f5..31e3d506ea 100644
--- src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php
+++ src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php
@@ -24,6 +24,14 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller {
 	 */
 	private $parent_post_type;
 
+	/**
+	 * Instance of a revision meta fields object.
+	 *
+	 * @since 4.7.0
+	 * @var WP_REST_Revision_Meta_Fields
+	 */
+	protected $meta;
+
 	/**
 	 * Parent controller.
 	 *
@@ -60,6 +68,7 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller {
 		$this->rest_base         = 'revisions';
 		$this->parent_base       = ! empty( $post_type_object->rest_base ) ? $post_type_object->rest_base : $post_type_object->name;
 		$this->namespace         = ! empty( $post_type_object->rest_namespace ) ? $post_type_object->rest_namespace : 'wp/v2';
+		$this->meta              = new WP_REST_Revision_Meta_Fields( 'revision' );
 	}
 
 	/**
@@ -619,6 +628,10 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller {
 			);
 		}
 
+		if ( rest_is_field_included( 'meta', $fields ) ) {
+			$data['meta'] = $this->meta->get_value( $post->ID, $request );
+		}
+
 		$context  = ! empty( $request['context'] ) ? $request['context'] : 'view';
 		$data     = $this->add_additional_fields_to_object( $data, $request );
 		$data     = $this->filter_response_by_context( $data, $context );
@@ -752,6 +765,8 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller {
 			$schema['properties']['guid'] = $parent_schema['properties']['guid'];
 		}
 
+		$schema['properties']['meta'] = $this->meta->get_field_schema();
+
 		$this->schema = $schema;
 
 		return $this->add_additional_fields_schema( $this->schema );
diff --git src/wp-includes/rest-api/fields/class-wp-rest-revision-meta-fields.php src/wp-includes/rest-api/fields/class-wp-rest-revision-meta-fields.php
new file mode 100644
index 0000000000..248e06e79b
--- /dev/null
+++ src/wp-includes/rest-api/fields/class-wp-rest-revision-meta-fields.php
@@ -0,0 +1,111 @@
+<?php
+/**
+ * REST API: WP_REST_Revision_Meta_Fields class
+ *
+ * @package WordPress
+ * @subpackage REST_API
+ * @since 6.4.0
+ */
+
+/**
+ * Core class used to manage meta values for revisions via the REST API.
+ *
+ * @since 6.4.0
+ *
+ * @see WP_REST_Meta_Fields
+ */
+class WP_REST_Revision_Meta_Fields extends WP_REST_Meta_Fields {
+
+	/**
+	 * Revision type to register fields for.
+	 *
+	 * @since 6.4.0
+	 * @var string
+	 */
+	protected $post_type;
+
+	/**
+	 * Constructor.
+	 *
+	 * @since 6.4.0
+	 *
+	 * @param string $post_type Revision type to register fields for.
+	 */
+	public function __construct( $post_type ) {
+		$this->post_type = $post_type;
+	}
+
+	/**
+	 * Retrieves the post meta type.
+	 *
+	 * @since 6.4.0
+	 *
+	 * @return string The meta type.
+	 */
+	protected function get_meta_type() {
+		return 'revision';
+	}
+
+	/**
+	 * Retrieves the post meta subtype.
+	 *
+	 * @since 6.4.0
+	 *
+	 * @return string Subtype for the meta type, or empty string if no specific subtype.
+	 */
+	protected function get_meta_subtype() {
+		return $this->post_type;
+	}
+
+	/**
+	 * Retrieves the type for register_rest_field().
+	 *
+	 * @since 6.4.0
+	 *
+	 * @see register_rest_field()
+	 *
+	 * @return string The REST field type.
+	 */
+	public function get_rest_field_type() {
+		return $this->post_type;
+	}
+
+	/**
+	 * Retrieves the meta field value.
+	 *
+	 * @since 4.7.0
+	 *
+	 * @param int             $object_id Object ID to fetch meta for.
+	 * @param WP_REST_Request $request   Full details about the request.
+	 * @return array Array containing the meta values keyed by name.
+	 */
+	public function get_value( $object_id, $request ) {
+		$data = get_post_meta( $object_id );
+		return $data;
+	}
+
+	/**
+	 * Retrieves raw metadata value for the specified object.
+	 *
+	 * @since 5.5.0
+	 *
+	 * @param string $meta_type Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user',
+	 *                          or any other object type with an associated meta table.
+	 * @param int    $object_id ID of the object metadata is for.
+	 * @param string $meta_key  Optional. Metadata key. If not specified, retrieve all metadata for
+	 *                          the specified object. Default empty string.
+	 * @param bool   $single    Optional. If true, return only the first value of the specified `$meta_key`.
+	 *                          This parameter has no effect if `$meta_key` is not specified. Default false.
+	 * @return mixed An array of values if `$single` is false.
+	 *               The value of the meta field if `$single` is true.
+	 *               False for an invalid `$object_id` (non-numeric, zero, or negative value),
+	 *               or if `$meta_type` is not specified.
+	 *               Null if the value does not exist.
+	 */
+	function get_metadata_raw( $meta_type, $object_id, $meta_key = '', $single = false ) {
+		$data = get_post_meta( $object_id, $request );
+		return $data;
+	}
+
+
+}
diff --git src/wp-includes/revision.php src/wp-includes/revision.php
index e8bbcdbce4..a5fe608bf6 100644
--- src/wp-includes/revision.php
+++ src/wp-includes/revision.php
@@ -361,15 +361,36 @@ function _wp_put_post_revision( $post = null, $autosave = false ) {
 		 * Fires once a revision has been saved.
 		 *
 		 * @since 2.6.0
+		 * @since 6.4.0 The post_id parameter was added.
 		 *
 		 * @param int $revision_id Post revision ID.
+		 * @param int $post_id     Post ID.
 		 */
-		do_action( '_wp_put_post_revision', $revision_id );
+		do_action( '_wp_put_post_revision', $revision_id, $post['post_parent'] );
 	}
 
 	return $revision_id;
 }
 
+
+/**
+ * Save the revisioned meta fields.
+ *
+ * @param int $revision_id The ID of the revision to save the meta to.
+ * @param int $post_id     The ID of the post the revision is associated with.
+ *
+ * @since 6.4.0
+ */
+function wp_save_revisioned_meta_fields( $revision_id, $post_id ) {
+
+	// Save revisioned meta fields.
+	foreach ( wp_post_revision_meta_keys() as $meta_key ) {
+		if ( metadata_exists( 'post', $post_id, $meta_key ) ) {
+			_wp_copy_post_meta( $post_id, $revision_id, $meta_key );
+		}
+	}
+}
+
 /**
  * Gets a post revision.
  *
@@ -450,6 +471,9 @@ function wp_restore_post_revision( $revision, $fields = null ) {
 	// Update last edit user.
 	update_post_meta( $post_id, '_edit_last', get_current_user_id() );
 
+	// Restore any revisioned meta fields.
+	wp_restore_post_revision_meta( $post_id, $revision['ID'] );
+
 	/**
 	 * Fires after a post revision has been restored.
 	 *
@@ -463,6 +487,85 @@ function wp_restore_post_revision( $revision, $fields = null ) {
 	return $post_id;
 }
 
+/**
+ * Restore the revisioned meta values for a post.
+ *
+ * @param int $post_id     The ID of the post to restore the meta to.
+ * @param int $revision_id The ID of the revision to restore the meta from.
+ *
+ * @since 6.4.0
+ */
+function wp_restore_post_revision_meta( $post_id, $revision_id ) {
+
+	// Restore revisioned meta fields.
+	foreach ( (array) wp_post_revision_meta_keys() as $meta_key ) {
+
+		// Clear any existing meta.
+		delete_post_meta( $post_id, $meta_key );
+
+		_wp_copy_post_meta( $revision_id, $post_id, $meta_key );
+	}
+}
+
+/**
+ * Copy post meta for the given key from one post to another.
+ *
+ * @param int    $source_post_id Post ID to copy meta value(s) from.
+ * @param int    $target_post_id Post ID to copy meta value(s) to.
+ * @param string $meta_key       Meta key to copy.
+ *
+ * @since 6.4.0
+ */
+function _wp_copy_post_meta( $source_post_id, $target_post_id, $meta_key ) {
+
+	foreach ( get_post_meta( $source_post_id, $meta_key ) as $meta_value ) {
+		/**
+		 * We use add_metadata() function vs add_post_meta() here
+		 * to allow for a revision post target OR regular post.
+		 */
+		add_metadata( 'post', $target_post_id, $meta_key, wp_slash( $meta_value ) );
+	}
+}
+
+/**
+ * Determine which post meta fields should be revisioned.
+ *
+ * @since 6.4.0
+ *
+ * @return array An array of meta keys to be revisioned.
+ */
+function wp_post_revision_meta_keys() {
+	global $wp_revisioned_meta_keys;
+
+	/**
+	 * Filter the list of post meta keys to be revisioned.
+	 *
+	 * @since 6.4.0
+	 *
+	 * @param array $keys An array of meta fields to be revisioned.
+	 */
+	return apply_filters( 'wp_post_revision_meta_keys', $wp_revisioned_meta_keys );
+}
+
+/**
+ * Check whether revisioned post meta fields have changed.
+ *
+ * @param bool    $post_has_changed Whether the post has changed.
+ * @param WP_Post $last_revision    The last revision post object.
+ * @param WP_Post $post             The post object.
+ *
+ * @since 6.4.0
+ */
+function wp_check_revisioned_meta_fields_have_changed( $post_has_changed, WP_Post $last_revision, WP_Post $post ) {
+	foreach ( wp_post_revision_meta_keys() as $meta_key ) {
+		if ( get_post_meta( $post->ID, $meta_key ) !== get_post_meta( $last_revision->ID, $meta_key ) ) {
+			$post_has_changed = true;
+			break;
+		}
+	}
+	return $post_has_changed;
+}
+
 /**
  * Deletes a revision.
  *
@@ -728,6 +831,7 @@ function _set_preview( $post ) {
 
 	add_filter( 'get_the_terms', '_wp_preview_terms_filter', 10, 3 );
 	add_filter( 'get_post_metadata', '_wp_preview_post_thumbnail_filter', 10, 3 );
+	add_filter( 'get_post_metadata', '_wp_preview_meta_filter', 10, 4 );
 
 	return $post;
 }
@@ -948,3 +1052,38 @@ function _wp_upgrade_revisions_of_post( $post, $revisions ) {
 
 	return true;
 }
+
+/**
+ * Filters preview post meta retrieval to get values from the autosave.
+ *
+ * Filters revisioned meta keys only.
+ *
+ * @since 6.4.0
+ *
+ * @param mixed  $value     Meta value to filter.
+ * @param int    $object_id Object ID.
+ * @param string $meta_key  Meta key to filter a value for.
+ * @param bool   $single    Whether to return a single value. Default false.
+ * @return mixed Original meta value if the meta key isn't revisioned, the object doesn't exist,
+ *               the post type is a revision or the post ID doesn't match the object ID.
+ *               Otherwise, the revisioned meta value is returned for the preview.
+ */
+function _wp_preview_meta_filter( $value, $object_id, $meta_key, $single ) {
+
+	$post = get_post();
+	if (
+		empty( $post ) ||
+		$post->ID !== $object_id ||
+		! in_array( $meta_key, wp_post_revision_meta_keys(), true ) ||
+		'revision' === $post->post_type
+	) {
+		return $value;
+	}
+
+	$preview = wp_get_post_autosave( $post->ID );
+	if ( ! is_object( $preview ) ) {
+		return $value;
+	}
+
+	return get_post_meta( $preview->ID, $meta_key, $single );
+}
diff --git src/wp-settings.php src/wp-settings.php
index 2479420742..aec891e6b2 100644
--- src/wp-settings.php
+++ src/wp-settings.php
@@ -306,6 +306,7 @@ require ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-comment-meta-fields.ph
 require ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-post-meta-fields.php';
 require ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-term-meta-fields.php';
 require ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-user-meta-fields.php';
+require ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-revision-meta-fields.php';
 require ABSPATH . WPINC . '/rest-api/search/class-wp-rest-search-handler.php';
 require ABSPATH . WPINC . '/rest-api/search/class-wp-rest-post-search-handler.php';
 require ABSPATH . WPINC . '/rest-api/search/class-wp-rest-term-search-handler.php';
diff --git tests/phpunit/tests/meta/registerMeta.php tests/phpunit/tests/meta/registerMeta.php
index ae2c1d6e63..9a127295b0 100644
--- tests/phpunit/tests/meta/registerMeta.php
+++ tests/phpunit/tests/meta/registerMeta.php
@@ -97,6 +97,7 @@ class Tests_Meta_Register_Meta extends WP_UnitTestCase {
 						'sanitize_callback' => null,
 						'auth_callback'     => '__return_true',
 						'show_in_rest'      => false,
+						'revisions_enabled' => false,
 					),
 				),
 			),
@@ -121,6 +122,7 @@ class Tests_Meta_Register_Meta extends WP_UnitTestCase {
 						'sanitize_callback' => null,
 						'auth_callback'     => '__return_true',
 						'show_in_rest'      => false,
+						'revisions_enabled' => false,
 					),
 				),
 			),
@@ -175,6 +177,7 @@ class Tests_Meta_Register_Meta extends WP_UnitTestCase {
 						'sanitize_callback' => array( $this, '_new_sanitize_meta_cb' ),
 						'auth_callback'     => '__return_true',
 						'show_in_rest'      => false,
+						'revisions_enabled' => false,
 					),
 				),
 			),
@@ -342,6 +345,7 @@ class Tests_Meta_Register_Meta extends WP_UnitTestCase {
 						'sanitize_callback' => null,
 						'auth_callback'     => '__return_true',
 						'show_in_rest'      => false,
+						'revisions_enabled' => false,
 					),
 				),
 			),
@@ -395,6 +399,7 @@ class Tests_Meta_Register_Meta extends WP_UnitTestCase {
 						'sanitize_callback' => null,
 						'auth_callback'     => '__return_true',
 						'show_in_rest'      => false,
+						'revisions_enabled' => false,
 					),
 				),
 			),
diff --git tests/phpunit/tests/post/metaRevisions.php tests/phpunit/tests/post/metaRevisions.php
new file mode 100644
index 0000000000..c132f01e40
--- /dev/null
+++ tests/phpunit/tests/post/metaRevisions.php
@@ -0,0 +1,571 @@
+<?php
+
+/**
+ *
+ * Tests for post meta revisioning.
+ *
+ * @group post
+ * @group revision
+ * @group meta
+ * @group meta-revisions
+ */
+class MetaRevisionTests extends WP_UnitTestCase {
+
+	/**
+	 * Callback function to add the revisioned keys.
+	 *
+	 * @param array $keys The array of revisioned keys.
+	 *
+	 * @return array
+	 */
+	public function add_revisioned_keys( $keys ) {
+		$keys[] = 'meta_revision_test';
+		$keys[] = 'meta_multiples_test';
+		return $keys;
+	}
+
+	/**
+	 * Test the revisions system for storage of meta values with slashes.
+	 *
+	 * @param string $passed   The passed data for testing.
+	 *
+	 * @param string $expected The expected value after storing & retrieving.
+	 *
+	 * @group revision
+	 * @group slashed
+	 * @dataProvider slashed_data_provider
+	 */
+	public function test_revisions_stores_meta_values_with_slashes( $passed, $expected ) {
+		// Set up a new post.
+		$post_id = $this->factory->post->create();
+
+		// And update to store an initial revision.
+		wp_update_post(
+			array(
+				'post_content' => 'some initial content',
+				'ID'           => $post_id,
+			)
+		);
+		add_filter( 'wp_post_revision_meta_keys', array( $this, 'add_revisioned_keys' ) );
+
+		// Store a custom meta value, which is not revisioned by default.
+		update_post_meta( $post_id, 'meta_revision_test', wp_slash( $passed ) );
+		$this->assertEquals( $expected, get_post_meta( $post_id, 'meta_revision_test', true ) );
+
+		// Update the post, storing a revision.
+		wp_update_post(
+			array(
+				'post_content' => 'some more content',
+				'ID'           => $post_id,
+			)
+		);
+
+		// Overwrite.
+		update_post_meta( $post_id, 'meta_revision_test', 'original' );
+		// Update the post, storing a revision.
+		wp_update_post(
+			array(
+				'post_content' => 'some more content again',
+				'ID'           => $post_id,
+			)
+		);
+
+		// Restore the previous revision.
+		$revisions = (array) wp_get_post_revisions( $post_id );
+
+		// Go back two to load the previous revision.
+		array_shift( $revisions );
+		$last_revision = array_shift( $revisions );
+
+		// Restore!
+		wp_restore_post_revision( $last_revision->ID );
+
+		$this->assertEquals( $expected, get_post_meta( $post_id, 'meta_revision_test', true ) );
+		remove_filter( 'wp_post_revision_meta_keys', array( $this, 'add_revisioned_keys' ) );
+	}
+
+	/**
+	 * Provide data for the slashed data tests.
+	 */
+	public function slashed_data_provider() {
+		return array(
+			array(
+				'some\text',
+				'some\text',
+			),
+			array(
+				'test some\ \\extra \\\slashed \\\\text ',
+				'test some\ \\extra \\\slashed \\\\text ',
+			),
+			array(
+				"This \'is\' an example \n of a \"quoted\" string",
+				"This \'is\' an example \n of a \"quoted\" string",
+			),
+			array(
+				'some unslashed text just to test! % & * ( ) #',
+				'some unslashed text just to test! % & * ( ) #',
+			),
+		);
+	}
+
+	/**
+	 * Test the revisions system for storage of meta values.
+	 *
+	 * @group revision
+	 */
+	public function test_revisions_stores_meta_values() {
+		/*
+		 * Set Up.
+		 */
+
+		// Set up a new post.
+		$post_id          = $this->factory->post->create();
+		$original_post_id = $post_id;
+
+		// And update to store an initial revision.
+		wp_update_post(
+			array(
+				'post_content' => 'some initial content',
+				'ID'           => $post_id,
+			)
+		);
+
+		// One revision so far.
+		$revisions = wp_get_post_revisions( $post_id );
+		$this->assertCount( 1, $revisions );
+
+		/*
+		 * First set up a meta value.
+		 */
+
+		// Store a custom meta value, which is not revisioned by default.
+		update_post_meta( $post_id, 'meta_revision_test', 'original' );
+
+		// Update the post, storing a revision.
+		wp_update_post(
+			array(
+				'post_content' => 'some more content',
+				'ID'           => $post_id,
+			)
+		);
+
+		$revisions = wp_get_post_revisions( $post_id );
+		$this->assertCount( 2, $revisions );
+
+		// Next, store some updated meta values for the same key.
+		update_post_meta( $post_id, 'meta_revision_test', 'update1' );
+
+		// Save the post, changing content to force a revision.
+		wp_update_post(
+			array(
+				'post_content' => 'some updated content',
+				'ID'           => $post_id,
+			)
+		);
+
+		$revisions = wp_get_post_revisions( $post_id );
+		$this->assertCount( 3, $revisions );
+
+		/*
+		 * Now restore the original revision.
+		 */
+
+		// Restore the previous revision.
+		$revisions = (array) wp_get_post_revisions( $post_id );
+
+		// Go back two to load the previous revision.
+		array_shift( $revisions );
+		$last_revision = array_shift( $revisions );
+
+		// Restore!
+		wp_restore_post_revision( $last_revision->ID );
+
+		wp_update_post( array( 'ID' => $post_id ) );
+		$revisions = wp_get_post_revisions( $post_id );
+		$this->assertCount( 4, $revisions );
+
+		/*
+		 * Check the meta values to verify they are NOT revisioned - they are not revisioned by default.
+		 */
+
+		// Custom post meta should NOT be restored, orignal value should not be restored, value still 'update1'.
+		$this->assertEquals( 'update1', get_post_meta( $post_id, 'meta_revision_test', true ) );
+
+		update_post_meta( $post_id, 'meta_revision_test', 'update2' );
+
+		/*
+		 * Test the revisioning of custom meta when enabled by the wp_post_revision_meta_keys filter.
+		 */
+
+		// Add the custom field to be revised via the wp_post_revision_meta_keys filter.
+		add_filter( 'wp_post_revision_meta_keys', array( $this, 'add_revisioned_keys' ) );
+
+		// Save the post, changing content to force a revision.
+		wp_update_post(
+			array(
+				'post_content' => 'more updated content',
+				'ID'           => $post_id,
+			)
+		);
+
+		$revisions = array_values( wp_get_post_revisions( $post_id ) );
+		$this->assertCount( 5, $revisions );
+		$this->assertEquals( 'update2', get_post_meta( $revisions[0]->ID, 'meta_revision_test', true ) );
+
+		// Store custom meta values, which should now be revisioned.
+		update_post_meta( $post_id, 'meta_revision_test', 'update3' );
+
+		/*
+		 * Save the post again, custom meta should now be revisioned.
+		 *
+		 * Note that a revision is saved even though there is no change
+		 * in post content, because the revisioned post_meta has changed.
+		 */
+		wp_update_post(
+			array(
+				'ID' => $post_id,
+			)
+		);
+
+		// This revision contains the existing post meta ('update3').
+		$revisions = wp_get_post_revisions( $post_id );
+		$this->assertCount( 6, $revisions );
+
+		// Verify that previous post meta is set.
+		$this->assertEquals( 'update3', get_post_meta( $post_id, 'meta_revision_test', true ) );
+
+		// Restore the previous revision.
+		$revisions = wp_get_post_revisions( $post_id );
+
+		// Go back two to load the previous revision.
+		array_shift( $revisions );
+		$last_revision = array_shift( $revisions );
+		wp_restore_post_revision( $last_revision->ID );
+
+		/*
+		 * Verify that previous post meta is restored.
+		 */
+		$this->assertEquals( 'update2', get_post_meta( $post_id, 'meta_revision_test', true ) );
+
+		// Try storing a blank meta.
+		update_post_meta( $post_id, 'meta_revision_test', '' );
+		wp_update_post(
+			array(
+				'ID' => $post_id,
+			)
+		);
+
+		update_post_meta( $post_id, 'meta_revision_test', 'update 4' );
+		wp_update_post(
+			array(
+				'ID' => $post_id,
+			)
+		);
+
+		// Restore the previous revision.
+		$revisions = wp_get_post_revisions( $post_id );
+		array_shift( $revisions );
+		$last_revision = array_shift( $revisions );
+		wp_restore_post_revision( $last_revision->ID );
+
+		/*
+		 * Verify that previous blank post meta is restored.
+		 */
+		$this->assertEquals( '', get_post_meta( $post_id, 'meta_revision_test', true ) );
+
+		/*
+		 * Test not tracking a key - remove the key from the revisioned meta.
+		 */
+		remove_all_filters( 'wp_post_revision_meta_keys' );
+
+		// Meta should no longer be revisioned.
+		update_post_meta( $post_id, 'meta_revision_test', 'update 5' );
+		wp_update_post(
+			array(
+				'ID'           => $post_id,
+				'post_content' => 'changed content',
+			)
+		);
+		update_post_meta( $post_id, 'meta_revision_test', 'update 6' );
+		wp_update_post(
+			array(
+				'ID'           => $post_id,
+				'post_content' => 'go updated content',
+			)
+		);
+
+		// Restore the previous revision.
+		$revisions = wp_get_post_revisions( $post_id );
+		array_shift( $revisions );
+		$last_revision = array_shift( $revisions );
+		wp_restore_post_revision( $last_revision->ID );
+
+		/*
+		 * Verify that previous post meta is NOT restored.
+		 */
+		$this->assertEquals( 'update 6', get_post_meta( $post_id, 'meta_revision_test', true ) );
+
+		// Add the custom field to be revised via the wp_post_revision_meta_keys filter.
+		add_filter( 'wp_post_revision_meta_keys', array( $this, 'add_revisioned_keys' ) );
+
+		/*
+		 * Test the revisioning of multiple meta keys.
+		 */
+
+		// Add three values for meta.
+		update_post_meta( $post_id, 'meta_revision_test', 'update 7' );
+		add_post_meta( $post_id, 'meta_revision_test', 'update 7 number 2' );
+		add_post_meta( $post_id, 'meta_revision_test', 'update 7 number 3' );
+		wp_update_post( array( 'ID' => $post_id ) );
+
+		// Update all three values.
+		update_post_meta( $post_id, 'meta_revision_test', 'update 8', 'update 7' );
+		update_post_meta( $post_id, 'meta_revision_test', 'update 8 number 2', 'update 7 number 2' );
+		update_post_meta( $post_id, 'meta_revision_test', 'update 8 number 3', 'update 7 number 3' );
+
+		// Restore the previous revision.
+		$revisions     = wp_get_post_revisions( $post_id );
+		$last_revision = array_shift( $revisions );
+		wp_restore_post_revision( $last_revision->ID );
+
+		/*
+		 * Verify that multiple metas stored correctly.
+		 */
+		$this->assertEquals( array( 'update 7', 'update 7 number 2', 'update 7 number 3' ), get_post_meta( $post_id, 'meta_revision_test' ) );
+
+		/*
+		 * Test the revisioning of a multidimensional array.
+		 */
+		$test_array = array(
+			'a' => array(
+				'1',
+				'2',
+				'3',
+			),
+			'b' => 'ok',
+			'c' => array(
+				'multi' => array(
+					'a',
+					'b',
+					'c',
+				),
+				'not'   => 'ok',
+			),
+		);
+
+		// Clear any old value.
+		delete_post_meta( $post_id, 'meta_revision_test' );
+
+		// Set the test meta to the array.
+		update_post_meta( $post_id, 'meta_revision_test', $test_array );
+
+		// Update to save.
+		wp_update_post( array( 'ID' => $post_id ) );
+
+		// Set the test meta blank.
+		update_post_meta( $post_id, 'meta_revision_test', '' );
+
+		// Restore the previous revision.
+		$revisions     = wp_get_post_revisions( $post_id );
+		$last_revision = array_shift( $revisions );
+		wp_restore_post_revision( $last_revision->ID );
+
+		/*
+		 * Verify  multidimensional array stored correctly.
+		 */
+		$stored_array = get_post_meta( $post_id, 'meta_revision_test' );
+		$this->assertEquals( $test_array, $stored_array[0] );
+		/*
+
+		 * Test multiple revisions on the same key.
+		 */
+
+		// Set the test meta to the array.
+		add_post_meta( $post_id, 'meta_multiples_test', 'test1' );
+		add_post_meta( $post_id, 'meta_multiples_test', 'test2' );
+		add_post_meta( $post_id, 'meta_multiples_test', 'test3' );
+
+		// Update to save.
+		wp_update_post( array( 'ID' => $post_id ) );
+
+		$stored_array = get_post_meta( $post_id, 'meta_multiples_test' );
+		$expect       = array( 'test1', 'test2', 'test3' );
+
+		$this->assertEquals( $expect, $stored_array );
+
+		// Restore the previous revision.
+		$revisions     = wp_get_post_revisions( $post_id );
+		$last_revision = array_shift( $revisions );
+		wp_restore_post_revision( $last_revision->ID );
+
+		$stored_array = get_post_meta( $post_id, 'meta_multiples_test' );
+		$expect       = array( 'test1', 'test2', 'test3' );
+
+		$this->assertEquals( $expect, $stored_array );
+
+		// Cleanup!
+		wp_delete_post( $original_post_id );
+	}
+
+	/**
+	 * Verify that only existing meta is revisioned.
+	 */
+	public function only_existing_meta_is_revisioned() {
+		add_filter( 'wp_post_revision_meta_keys', array( $this, 'add_revisioned_keys' ) );
+
+		// Set up a new post.
+		$post_id = $this->factory->post->create(
+			array(
+				'post_content' => 'initial content',
+			)
+		);
+
+		// Revision v1.
+		wp_update_post(
+			array(
+				'ID'           => $post_id,
+				'post_content' => 'updated content v1',
+			)
+		);
+
+		$this->assertPostNotHasMetaKey( $post_id, 'foo' );
+		$this->assertPostNotHasMetaKey( $post_id, 'bar' );
+
+		$revisions = wp_get_post_revisions( $post_id );
+		$revision  = array_shift( $revisions );
+		$this->assertEmpty( get_metadata( 'post', $revision->ID ) );
+
+		// Revision v2.
+		wp_update_post(
+			array(
+				'ID'           => $post_id,
+				'post_content' => 'updated content v2',
+				'meta_input'   => array(
+					'foo' => 'foo v2',
+				),
+			)
+		);
+
+		$this->assertPostHasMetaKey( $post_id, 'foo' );
+		$this->assertPostNotHasMetaKey( $post_id, 'bar' );
+		$this->assertPostNotHasMetaKey( $post_id, 'meta_revision_test' );
+
+		$revisions = wp_get_post_revisions( $post_id );
+		$revision  = array_shift( $revisions );
+		$this->assertPostHasMetaKey( $revision->ID, 'foo' );
+		$this->assertPostNotHasMetaKey( $revision->ID, 'bar' );
+		$this->assertPostNotHasMetaKey( $revision->ID, 'meta_revision_test' );
+	}
+
+	/**
+	 * Verify that blank strings are revisioned correctly.
+	 */
+	public function blank_meta_is_revisioned() {
+
+		add_filter( 'wp_post_revision_meta_keys', array( $this, 'add_revisioned_keys' ) );
+
+		// Set up a new post.
+		$post_id = $this->factory->post->create(
+			array(
+				'post_content' => 'initial content',
+				'meta_input'   => array(
+					'foo' => 'foo',
+				),
+			)
+		);
+
+		// Set the test meta to an empty string.
+		update_post_meta( $post_id, 'foo', '' );
+
+		// Update to save.
+		wp_update_post( array( 'ID' => $post_id ) );
+
+		$stored_array = get_post_meta( $post_id, 'meta_multiples_test' );
+		$expect       = array( 'test1', 'test2', 'test3' );
+
+		$this->assertEquals( $expect, $stored_array );
+
+		// Restore the previous revision.
+		$revisions     = wp_get_post_revisions( $post_id );
+		$last_revision = array_shift( $revisions );
+		wp_restore_post_revision( $last_revision->ID );
+		$stored_data = get_post_meta( $post_id, 'foo' );
+		$this->assertEquals( '', $stored_data[0] );
+	}
+
+
+	/**
+	 * @dataProvider data_register_post_meta_supports_revisions
+	 */
+	public function test_register_post_meta_supports_revisions( $post_type, $meta_key, $args, $expected_is_revisioned ) {
+		register_post_meta( $post_type, $meta_key, $args );
+
+		// Set up a new post.
+		$post_id = $this->factory->post->create(
+			array(
+				'post_content' => 'initial content',
+				'post_type'    => $post_type,
+				'meta_input'   => array(
+					$meta_key => 'foo',
+				),
+			)
+		);
+
+		// Update the post meta and post to save.
+		update_post_meta( $post_id, $meta_key, 'bar' );
+		wp_update_post(
+			array(
+				'ID'         => $post_id,
+				'post_title' => 'updated title',
+			)
+		);
+
+		// Check the last revision for the post to see if the meta key was revisioned
+		$revisions       = wp_get_post_revisions( $post_id );
+		$revision        = array_shift( $revisions );
+		$revisioned_meta = get_post_meta( $revision->ID, $meta_key, true );
+		$this->assertEquals( $expected_is_revisioned, 'bar' === $revisioned_meta );
+
+		// Reset global so subsequent data tests do not get polluted.
+		$GLOBALS['wp_meta_keys'] = array();
+	}
+
+	public function data_register_post_meta_supports_revisions() {
+		return array(
+			array( 'post', 'registered_key1', array( 'single' => true ), false ),
+			array(
+				'post',
+				'registered_key1',
+				array(
+					'single'            => true,
+					'revisions_enabled' => true,
+				),
+				true,
+			),
+			array( 'page', 'registered_key2', array( 'revisions_enabled' => false ), false ),
+			array( 'page', 'registered_key2', array( 'revisions_enabled' => true ), true ),
+			array( '', 'registered_key3', array( 'revisions_enabled' => false ), false ),
+			array( '', 'registered_key3', array( 'revisions_enabled' => true ), true ),
+		);
+	}
+
+	/**
+	 * Assert the a post has a meta key.
+	 *
+	 * @param int    $post_id        The ID of the post to check.
+	 * @param string $meta_key The meta key to check for.
+	 */
+	protected function assertPostHasMetaKey( $post_id, $meta_key ) {
+		$this->assertEquals( $expect, $stored_array );
+	}
+
+	/**
+	 * Assert that post does not have a meta key.
+	 *
+	 * @param int    $post_id        The ID of the post to check.
+	 * @param string $meta_key The meta key to check for.
+	 */
+	protected function assertPostNotHasMetaKey( $post_id, $meta_key ) {
+		$this->assertArrayNotHasKey( $meta_key, get_metadata( 'post', $post_id ) );
+	}
+}
diff --git tests/phpunit/tests/rest-api/rest-post-meta-fields.php tests/phpunit/tests/rest-api/rest-post-meta-fields.php
index 2142e80582..a184ba2022 100644
--- tests/phpunit/tests/rest-api/rest-post-meta-fields.php
+++ tests/phpunit/tests/rest-api/rest-post-meta-fields.php
@@ -3112,4 +3112,159 @@ class WP_Test_REST_Post_Meta_Fields extends WP_Test_REST_TestCase {
 		}
 		return $query;
 	}
+
+
+	/**
+	 * Test that post meta is revisioned when saving to the posts REST API endpoint.
+	 *
+	 * @ticket 20564
+	 */
+	public function test_revisioned_post_meta_with_posts_endpoint() {
+		$this->grant_write_permission();
+
+		register_post_meta(
+			'post',
+			'foo',
+			array(
+				'single'            => true,
+				'show_in_rest'      => true,
+				'revisions_enabled' => true,
+			)
+		);
+
+		$post_id = self::$post_id;
+
+		// Update the post, saving the meta.
+		$request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) );
+		$request->set_body_params(
+			array(
+				'title' => 'Revision 1',
+				'meta'  => array(
+					'foo' => 'bar',
+				),
+			)
+		);
+		$response = rest_get_server()->dispatch( $request );
+		$this->assertSame( 200, $response->get_status() );
+
+		// Get the last revision.
+		$revisions   = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) );
+		$revision_id = array_shift( $revisions )->ID;
+
+		// @todo Ensure the revisions endpoint returns the correct meta values
+		// Check that the revisions endpoint returns the correct meta value.
+		$request  = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id ) );
+		$response = rest_get_server()->dispatch( $request );
+		$this->assertSame( 200, $response->get_status() );
+		$data = $response->get_data();
+		$this->assertSame( array( 'bar' ), $response->get_data()['meta']['foo'] );
+
+		// Check that the post meta is set correctly.
+		$this->assertSame( 'bar', get_post_meta( $revision_id, 'foo', true ) );
+
+		// Create two more revisions with different meta values for the foo key.
+		$request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) );
+		$request->set_body_params(
+			array(
+				'title' => 'Revision 2',
+				'meta'  => array(
+					'foo' => 'baz',
+				),
+			)
+		);
+		$response = rest_get_server()->dispatch( $request );
+		$this->assertSame( 200, $response->get_status() );
+
+		// Get the last revision.
+		$revisions     = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) );
+		$revision_id_2 = array_shift( $revisions )->ID;
+
+		// Check that the revision has the correct meta value.
+		$request  = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id_2 ) );
+		$response = rest_get_server()->dispatch( $request );
+		$this->assertSame( 200, $response->get_status() );
+		$this->assertSame( array( 'baz' ), $response->get_data()['meta']['foo'] );
+
+		// Check that the post meta is set correctly.
+		$this->assertSame( 'baz', get_post_meta( $revision_id_2, 'foo', true ) );
+
+		// One more revision!
+		$request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) );
+		$request->set_body_params(
+			array(
+				'title' => 'Revision 3',
+				'meta'  => array(
+					'foo' => 'qux',
+				),
+			)
+		);
+		$response = rest_get_server()->dispatch( $request );
+		$this->assertSame( 200, $response->get_status() );
+
+		// Get the last revision.
+		$revisions     = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) );
+		$revision_id_3 = array_shift( $revisions )->ID;
+
+		// Check that the revision has the correct meta value.
+		$request  = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id_3 ) );
+		$response = rest_get_server()->dispatch( $request );
+		$this->assertSame( 200, $response->get_status() );
+		$this->assertSame( array( 'qux' ), $response->get_data()['meta']['foo'] );
+
+		// Check that the post meta is set correctly.
+		$this->assertSame( 'qux', get_post_meta( $revision_id_3, 'foo', true ) );
+
+		// Restore Revision 3 and verify the post gets the correct meta value.
+		wp_restore_post_revision( $revision_id_3 );
+		$this->assertSame( 'qux', get_post_meta( $post_id, 'foo', true ) );
+
+		// Restore Revision 2 and verify the post gets the correct meta value.
+		wp_restore_post_revision( $revision_id_2 );
+		$this->assertSame( 'baz', get_post_meta( $post_id, 'foo', true ) );
+
+		// Test with multiple meta values. Note that the posts endpoint doesn't accept multiple meta values, so we'll add those manually.
+		$request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) );
+		$request->set_body_params(
+			array(
+				'title' => 'Revision 4',
+				'meta'  => array(
+					'foo' => 'bar',
+				),
+			)
+		);
+		$response = rest_get_server()->dispatch( $request );
+		$this->assertSame( 200, $response->get_status() );
+
+		// Add additional meta values.
+		add_post_meta( $post_id, 'foo', 'bat' );
+		add_post_meta( $post_id, 'foo', 'baz' );
+
+		// Log the current post meta.
+		$meta = get_post_meta( $post_id );
+
+		// Update the post.
+		$request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) );
+		$request->set_body_params(
+			array(
+				'title' => 'Revision 4 update',
+			)
+		);
+		$response = rest_get_server()->dispatch( $request );
+		$this->assertSame( 200, $response->get_status() );
+
+		// Get the last revision.
+		$revisions     = wp_get_post_revisions( $post_id, array( 'posts_per_page' => 1 ) );
+		$revision_id_4 = array_shift( $revisions )->ID;
+
+		// Check that the revision has the correct meta value.
+		$request  = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d/revisions/%d', $post_id, $revision_id_4 ) );
+		$response = rest_get_server()->dispatch( $request );
+		$this->assertSame( 200, $response->get_status() );
+
+		$this->assertSame(
+			array( 'bar', 'bat', 'baz' ),
+			$response->get_data()['meta']['foo']
+		);
+
+	}
 }
diff --git tests/phpunit/tests/user/wpRegisterPersistedPreferencesMeta.php tests/phpunit/tests/user/wpRegisterPersistedPreferencesMeta.php
index c3c4bd7a09..68b3c86742 100644
--- tests/phpunit/tests/user/wpRegisterPersistedPreferencesMeta.php
+++ tests/phpunit/tests/user/wpRegisterPersistedPreferencesMeta.php
@@ -52,6 +52,7 @@ class Tests_User_WpRegisterPersistedPreferencesMeta extends WP_UnitTestCase {
 						'additionalProperties' => true,
 					),
 				),
+				'revisions_enabled' => false,
 			),
 			$wp_meta_keys['user'][''][ $meta_key ],
 			'The registered metadata did not have the expected structure'
