Index: src/js/_enqueues/admin/site-health.js
===================================================================
--- src/js/_enqueues/admin/site-health.js	(revision 45158)
+++ src/js/_enqueues/admin/site-health.js	(working copy)
@@ -9,8 +9,8 @@
 jQuery( document ).ready( function( $ ) {
 
 	var data;
-
 	var clipboard = new ClipboardJS( '.site-health-copy-buttons .copy-button' );
+	var isDebugTab = $( '.health-check-body.health-check-debug-tab' ).length;
 
 	// Debug information copy section.
 	clipboard.on( 'success', function( e ) {
@@ -118,16 +118,18 @@
 
 		$progressCount.text( val + '%' );
 
-		$.post(
-			ajaxurl,
-			{
-				'action': 'health-check-site-status-result',
-				'_wpnonce': SiteHealth.nonce.site_status_result,
-				'counts': SiteHealth.site_status.issues
-			}
-		);
+		if ( ! isDebugTab ) {
+			$.post(
+				ajaxurl,
+				{
+					'action': 'health-check-site-status-result',
+					'_wpnonce': SiteHealth.nonce.site_status_result,
+					'counts': SiteHealth.site_status.issues
+				}
+			);
 
-		wp.a11y.speak( SiteHealth.string.site_health_complete_screen_reader.replace( '%s', val + '%' ) );
+			wp.a11y.speak( SiteHealth.string.site_health_complete_screen_reader.replace( '%s', val + '%' ) );
+		}
 	}
 
 	/**
@@ -171,7 +173,7 @@
 		}
 	}
 
-	if ( 'undefined' !== typeof SiteHealth ) {
+	if ( 'undefined' !== typeof SiteHealth && ! isDebugTab ) {
 		if ( 0 === SiteHealth.site_status.direct.length && 0 === SiteHealth.site_status.async.length ) {
 			RecalculateProgression();
 		} else {
@@ -209,4 +211,77 @@
 		}
 	}
 
+	function getDirectorySizes() {
+		var data = {
+			action: 'health-check-get-sizes',
+			_wpnonce: SiteHealth.nonce.site_status_result
+		};
+
+		var timestamp = ( new Date().getTime() );
+
+		// After 3 seconds announce that we're still waiting for directory sizes.
+		var timeout = window.setTimeout( function() {
+			wp.a11y.speak( SiteHealth.string.please_wait );
+		}, 3000 );
+
+		$.post( {
+			type: 'POST',
+			url: ajaxurl,
+			data: data,
+			dataType: 'json'
+		} ).done( function( response ) {
+			updateDirSizes( response.data || {} );
+		} ).always( function() {
+			var delay = ( new Date().getTime() ) - timestamp;
+
+			$( '.health-check-wp-paths-sizes.spinner' ).css( 'visibility', 'hidden' );
+			RecalculateProgression();
+
+			if ( delay > 3000  ) {
+				// We have announced that we're waiting.
+				// Announce that we're ready after giving at least 3 seconds for the first announcement
+				// to be read out, or the two may collide.
+				if ( delay > 6000 ) {
+					delay = 0;
+				} else {
+					delay = 6500 - delay;
+				}
+
+				window.setTimeout( function() {
+					wp.a11y.speak( SiteHealth.string.site_health_complete );
+				}, delay );
+			} else {
+				// Cancel the announcement.
+				window.clearTimeout( timeout );
+			}
+		} );
+	}
+
+	function updateDirSizes( data ) {
+		var copyButton = $( 'button.button.copy-button' );
+		var clipdoardText = copyButton.attr( 'data-clipboard-text' );
+
+		$.each( data, function( name, value ) {
+			var text = value.debug || value.size;
+
+			if ( typeof text !== 'undefined' ) {
+				clipdoardText = clipdoardText.replace( name + ': not calculated', name + ': ' + text );
+			}
+		} );
+
+		copyButton.attr( 'data-clipboard-text', clipdoardText );
+
+		$( '#health-check-accordion-block-wp-paths-sizes' ).find( 'td[class]' ).each( function( i, element ) {
+			var td = $( element );
+			var name = td.attr( 'class' );
+
+			if ( data.hasOwnProperty( name ) && data[ name ].size ) {
+				td.text( data[ name ].size );
+			}
+		} );
+	}
+
+	if ( isDebugTab ) {
+		getDirectorySizes();
+	}
 } );
Index: src/wp-admin/admin-ajax.php
===================================================================
--- src/wp-admin/admin-ajax.php	(revision 45158)
+++ src/wp-admin/admin-ajax.php	(working copy)
@@ -136,6 +136,7 @@
 	'health-check-is-in-debug-mode',
 	'health-check-background-updates',
 	'health-check-loopback-requests',
+	'health-check-get-sizes',
 );
 
 // Deprecated
Index: src/wp-admin/css/site-health.css
===================================================================
--- src/wp-admin/css/site-health.css	(revision 45158)
+++ src/wp-admin/css/site-health.css	(working copy)
@@ -391,6 +391,13 @@
 	margin-left: 22px;
 }
 
+.health-check-wp-paths-sizes.spinner {
+	position: absolute;
+	visibility: visible;
+	float: none;
+	margin: 0 4px;
+}
+
 @media screen and (max-width: 782px) {
 	.health-check-body {
 		margin: 0 12px;
Index: src/wp-admin/includes/ajax-actions.php
===================================================================
--- src/wp-admin/includes/ajax-actions.php	(revision 45158)
+++ src/wp-admin/includes/ajax-actions.php	(working copy)
@@ -4958,3 +4958,52 @@
 
 	wp_send_json_success();
 }
+
+/**
+ * Ajax handler for site health check to get directories and database sizes.
+ *
+ * @since 5.2.0
+ */
+function wp_ajax_health_check_get_sizes() {
+	check_ajax_referer( 'health-check-site-status-result' );
+
+	if ( ! current_user_can( 'install_plugins' ) ) {
+		wp_send_json_error();
+	}
+
+	if ( ! class_exists( 'WP_Debug_Data' ) ) {
+		require_once( ABSPATH . 'wp-admin/includes/class-wp-debug-data.php' );
+	}
+
+	$sizes_data = WP_Debug_Data::get_sizes();
+	$all_sizes  = array();
+
+	foreach ( $sizes_data as $name => $value ) {
+		$name = sanitize_text_field( $name );
+		$data = array();
+
+		if ( isset( $value['size'] ) ) {
+			if ( is_string( $value['size'] ) ) {
+				$data['size'] = sanitize_text_field( $value['size'] );
+			} else {
+				$data['size'] = (int) $value['size'];
+			}
+		}
+
+		if ( isset( $value['debug'] ) ) {
+			if ( is_string( $value['debug'] ) ) {
+				$data['debug'] = sanitize_text_field( $value['debug'] );
+			} else {
+				$data['debug'] = (int) $value['debug'];
+			}
+		}
+
+		$all_sizes[ $name ] = $data;
+	}
+
+	if ( isset( $all_sizes['total_size']['debug'] ) && 'not available' === $all_sizes['total_size']['debug'] ) {
+		wp_send_json_error( $all_sizes );
+	}
+
+	wp_send_json_success( $all_sizes );
+}
Index: src/wp-admin/includes/class-wp-debug-data.php
===================================================================
--- src/wp-admin/includes/class-wp-debug-data.php	(revision 45158)
+++ src/wp-admin/includes/class-wp-debug-data.php	(working copy)
@@ -385,110 +385,21 @@
 			);
 		}
 
-		$size_db = WP_Debug_Data::get_database_size();
+		$not_calculated = __( 'Not calculated' );
 
-		/*
-		 * We will be using the PHP max execution time to prevent the size calculations
-		 * from causing a timeout. The default value is 30 seconds, and some
-		 * hosts do not allow you to read configuration values.
-		 */
-		if ( function_exists( 'ini_get' ) ) {
-			$max_execution_time = ini_get( 'max_execution_time' );
-		}
-
-		// The max_execution_time defaults to 0 when PHP runs from cli.
-		// We still want to limit it below.
-		if ( empty( $max_execution_time ) ) {
-			$max_execution_time = 30;
-		}
-
-		// Here 20 seconds is a "sensible default" for how long to make the user wait for the directory size calculation.
-		// When testing 20 seconds seem enough in nearly all cases. The remaining edge cases are likely testing or development sites
-		// that have very large number of files, for example `node_modules` in plugins or themes, etc.
-		if ( $max_execution_time > 20 ) {
-			$max_execution_time = 20;
-		} elseif ( $max_execution_time > 10 ) {
-			// If the max_execution_time is set to lower than 20 seconds, reduce it a bit to prevent
-			// edge-case timeouts that may happen after the size loop has finished running.
-			$max_execution_time -= 1;
-		}
-
-		// Go through the various installation directories and calculate their sizes.
-		$size_directories = array(
-			'wordpress' => array(
-				'path' => ABSPATH,
-				'size' => 0,
-			),
-			'themes'    => array(
-				'path' => trailingslashit( get_theme_root() ),
-				'size' => 0,
-			),
-			'plugins'   => array(
-				'path' => trailingslashit( WP_PLUGIN_DIR ),
-				'size' => 0,
-			),
-			'uploads'   => array(
-				'path' => $upload_dir['basedir'],
-				'size' => 0,
-			),
-		);
-
-		$size_total = 0;
-
-		// Loop over all the directories we want to gather the sizes for.
-		foreach ( $size_directories as $size => $attributes ) {
-			$dir_size = null; // Default to timeout.
-
-			if ( microtime( true ) - WP_START_TIMESTAMP < $max_execution_time ) {
-				$dir_size = get_dirsize( $attributes['path'], $max_execution_time );
-			}
-
-			if ( false === $dir_size ) {
-				// Error reading.
-				$size_directories[ $size ]['size']  = __( 'The size cannot be calculated. The directory is not accessible. Usually caused by invalid permissions.' );
-				$size_directories[ $size ]['debug'] = 'not accessible';
-
-				// Stop total size calculation.
-				$size_total = null;
-			} elseif ( null === $dir_size ) {
-				// Timeout.
-				$size_directories[ $size ]['size']  = __( 'The directory size calculation has timed out. Usually caused by a very large number of sub-directories and files.' );
-				$size_directories[ $size ]['debug'] = 'timeout while calculating size';
-
-				// Stop total size calculation.
-				$size_total = null;
-			} else {
-				$is_subdir = ( strpos( $size_directories[ $size ]['path'], ABSPATH ) === 0 );
-
-				// phpcs:ignore WordPress.WP.CapitalPDangit.Misspelled
-				if ( null !== $size_total && ( 'wordpress' === $size || ! $is_subdir ) ) {
-					$size_total += $dir_size;
-				}
-
-				$size_directories[ $size ]['size']  = size_format( $dir_size, 2 );
-				$size_directories[ $size ]['debug'] = $size_directories[ $size ]['size'];
-			}
-		}
-
-		if ( null !== $size_total && $size_db > 0 ) {
-			$size_total = size_format( $size_total + $size_db, 2 );
-		} else {
-			$size_total = 0;
-		}
-
 		$info['wp-paths-sizes']['fields'] = array(
 			'uploads_path'       => array(
 				'label' => __( 'Uploads Directory Location' ),
-				'value' => $size_directories['uploads']['path'],
+				'value' => $upload_dir['basedir'],
 			),
 			'uploads_size'       => array(
 				'label' => __( 'Uploads Directory Size' ),
-				'value' => $size_directories['uploads']['size'],
-				'debug' => $size_directories['uploads']['debug'],
+				'value' => $not_calculated,
+				'debug' => 'not calculated',
 			),
 			'themes_path'        => array(
 				'label' => __( 'Themes Directory Location' ),
-				'value' => $size_directories['themes']['path'],
+				'value' => trailingslashit( get_theme_root() ),
 			),
 			'current_theme_path' => array(
 				'label' => __( 'Current Theme Directory' ),
@@ -496,35 +407,36 @@
 			),
 			'themes_size'        => array(
 				'label' => __( 'Themes Directory Size' ),
-				'value' => $size_directories['themes']['size'],
-				'debug' => $size_directories['themes']['debug'],
+				'value' => $not_calculated,
+				'debug' => 'not calculated',
 			),
 			'plugins_path'       => array(
 				'label' => __( 'Plugins Directory Location' ),
-				'value' => $size_directories['plugins']['path'],
+				'value' => trailingslashit( WP_PLUGIN_DIR ),
 			),
 			'plugins_size'       => array(
 				'label' => __( 'Plugins Directory Size' ),
-				'value' => $size_directories['plugins']['size'],
-				'debug' => $size_directories['plugins']['debug'],
+				'value' => $not_calculated,
+				'debug' => 'not calculated',
 			),
 			'wordpress_path'     => array(
 				'label' => __( 'WordPress Directory Location' ),
-				'value' => $size_directories['wordpress']['path'],
+				'value' => ABSPATH,
 			),
 			'wordpress_size'     => array(
 				'label' => __( 'WordPress Directory Size' ),
-				'value' => $size_directories['wordpress']['size'],
-				'debug' => $size_directories['wordpress']['debug'],
+				'value' => $not_calculated,
+				'debug' => 'not calculated',
 			),
 			'database_size'      => array(
 				'label' => __( 'Database size' ),
-				'value' => size_format( $size_db, 2 ),
+				'value' => $not_calculated,
+				'debug' => 'not calculated',
 			),
 			'total_size'         => array(
 				'label' => __( 'Total installation size' ),
-				'value' => $size_total > 0 ? $size_total : __( 'Total size is not available. Some errors were encountered when determining the size of your installation.' ),
-				'debug' => $size_total > 0 ? $size_total : 'not available',
+				'value' => $not_calculated,
+				'debug' => 'not calculated',
 			),
 		);
 
@@ -1186,4 +1098,127 @@
 
 		return (int) $size;
 	}
+
+	/**
+	 * Fetch the sizes of the WordPress directories: `wordpress` (ABSPATH), `plugins`, `themes`, and `uploads`.
+	 * Intended to supplement the array returned by `WP_Debug_Data::debug_data()`.
+	 *
+	 * @since 5.2.0
+	 *
+	 * @return array The sizes of the directories, also the database size and total installation size.
+	 */
+	public static function get_sizes() {
+		$size_db    = self::get_database_size();
+		$upload_dir = wp_get_upload_dir();
+
+		/*
+		 * We will be using the PHP max execution time to prevent the size calculations
+		 * from causing a timeout. The default value is 30 seconds, and some
+		 * hosts do not allow you to read configuration values.
+		 */
+		if ( function_exists( 'ini_get' ) ) {
+			$max_execution_time = ini_get( 'max_execution_time' );
+		}
+
+		// The max_execution_time defaults to 0 when PHP runs from cli.
+		// We still want to limit it below.
+		if ( empty( $max_execution_time ) ) {
+			$max_execution_time = 30;
+		}
+
+		if ( $max_execution_time > 20 ) {
+			// If the max_execution_time is set to lower than 20 seconds, reduce it a bit to prevent
+			// edge-case timeouts that may happen after the size loop has finished running.
+			$max_execution_time -= 2;
+		}
+
+		// Go through the various installation directories and calculate their sizes.
+		$all_sizes = array(
+			'wordpress_size' => array(
+				'path' => ABSPATH,
+				'size' => 0,
+			),
+			'themes_size'    => array(
+				'path' => trailingslashit( get_theme_root() ),
+				'size' => 0,
+			),
+			'plugins_size'   => array(
+				'path' => trailingslashit( WP_PLUGIN_DIR ),
+				'size' => 0,
+			),
+			'uploads_size'   => array(
+				'path' => $upload_dir['basedir'],
+				'size' => 0,
+			),
+		);
+
+		$size_total = 0;
+
+		// Loop over all the directories we want to gather the sizes for.
+		foreach ( $all_sizes as $name => $attributes ) {
+			$dir_size = null; // Default to timeout.
+
+			if ( microtime( true ) - WP_START_TIMESTAMP < $max_execution_time ) {
+				$dir_size = recurse_dirsize( $attributes['path'], null, $max_execution_time );
+			}
+
+			unset( $all_sizes[ $name ]['path'] );
+
+			if ( false === $dir_size ) {
+				// Error reading.
+				$all_sizes[ $name ]['size']  = __( 'The size cannot be calculated. The directory is not accessible. Usually caused by invalid permissions.' );
+				$all_sizes[ $name ]['debug'] = 'not accessible';
+
+				// Stop total size calculation.
+				$size_total = null;
+			} elseif ( null === $dir_size ) {
+				// Timeout.
+				$all_sizes[ $name ]['size']  = __( 'The directory size calculation has timed out. Usually caused by a very large number of sub-directories and files.' );
+				$all_sizes[ $name ]['debug'] = 'timeout while calculating size';
+
+				// Stop total size calculation.
+				$size_total = null;
+			} else {
+				$is_subdir = ( strpos( $all_sizes[ $name ]['path'], ABSPATH ) === 0 );
+
+				// phpcs:ignore WordPress.WP.CapitalPDangit.Misspelled
+				if ( null !== $size_total && ( 'wordpress' === $name || ! $is_subdir ) ) {
+					$size_total += $dir_size;
+				}
+
+				$all_sizes[ $name ]['size']  = size_format( $dir_size, 2 );
+				$all_sizes[ $name ]['debug'] = $all_sizes[ $name ]['size'];
+			}
+		}
+
+		if ( $size_db > 0 ) {
+			$database_size = size_format( $size_db, 2 );
+
+			$all_sizes['database_size'] = array(
+				'size'  => $database_size,
+				'debug' => $database_size,
+			);
+		} else {
+			$all_sizes['database_size'] = array(
+				'size'  => __( 'Not available' ),
+				'debug' => 'not available',
+			);
+		}
+
+		if ( null !== $size_total && $size_db > 0 ) {
+			$total_size = size_format( $size_total + $size_db, 2 );
+
+			$all_sizes['total_size'] = array(
+				'size'  => $total_size,
+				'debug' => $total_size,
+			);
+		} else {
+			$all_sizes['total_size'] = array(
+				'size'  => __( 'Total size is not available. Some errors were encountered when determining the size of your installation.' ),
+				'debug' => 'not available',
+			);
+		}
+
+		return $all_sizes;
+	}
 }
Index: src/wp-admin/site-health-info.php
===================================================================
--- src/wp-admin/site-health-info.php	(revision 45158)
+++ src/wp-admin/site-health-info.php	(working copy)
@@ -62,8 +62,9 @@
 	<p><?php _e( 'The Site Health check requires JavaScript.' ); ?></p>
 </div>
 
-<div class="health-check-body hide-if-no-js">
+<div class="health-check-body health-check-debug-tab hide-if-no-js">
 	<?php
+
 	WP_Debug_Data::check_for_updates();
 
 	$info = WP_Debug_Data::debug_data();
@@ -93,20 +94,36 @@
 	<div id="health-check-debug" class="health-check-accordion">
 
 		<?php
+
+		$sizes_fields = array( 'uploads_size', 'themes_size', 'plugins_size', 'wordpress_size', 'database_size', 'total_size' );
+
 		foreach ( $info as $section => $details ) {
 			if ( ! isset( $details['fields'] ) || empty( $details['fields'] ) ) {
 				continue;
 			}
+
 			?>
 			<h3 class="health-check-accordion-heading">
 				<button aria-expanded="false" class="health-check-accordion-trigger" aria-controls="health-check-accordion-block-<?php echo esc_attr( $section ); ?>" type="button">
 					<span class="title">
 						<?php echo esc_html( $details['label'] ); ?>
+						<?php
 
-						<?php if ( isset( $details['show_count'] ) && $details['show_count'] ) : ?>
-							<?php printf( '(%d)', count( $details['fields'] ) ); ?>
-						<?php endif; ?>
+						if ( isset( $details['show_count'] ) && $details['show_count'] ) {
+							printf( '(%d)', count( $details['fields'] ) );
+						}
+
+						?>
 					</span>
+					<?php
+
+					if ( 'wp-paths-sizes' === $section ) {
+						?>
+						<span class="health-check-wp-paths-sizes spinner" title="<?php esc_attr_e( 'Calculating directory sizes. This may take a while&hellip;' ); ?>"></span>
+						<?php
+					}
+
+					?>
 					<span class="icon"></span>
 				</button>
 			</h3>
@@ -113,46 +130,44 @@
 
 			<div id="health-check-accordion-block-<?php echo esc_attr( $section ); ?>" class="health-check-accordion-panel" hidden="hidden">
 				<?php
+
+				$kses_settings = array(
+					'a'      => array(
+						'href' => true,
+					),
+					'strong' => true,
+					'em'     => true,
+				);
+
 				if ( isset( $details['description'] ) && ! empty( $details['description'] ) ) {
-					printf(
-						'<p>%s</p>',
-						wp_kses(
-							$details['description'],
-							array(
-								'a'      => array(
-									'href' => true,
-								),
-								'strong' => true,
-								'em'     => true,
-							)
-						)
-					);
+					printf( '<p>%s</p>', wp_kses( $details['description'], $kses_settings ) );
 				}
+
 				?>
 				<table class="widefat striped health-check-table" role="presentation">
 					<tbody>
 					<?php
-					foreach ( $details['fields'] as $field ) {
+
+					foreach ( $details['fields'] as $field_name => $field ) {
 						if ( is_array( $field['value'] ) ) {
 							$values = '<ul>';
+
 							foreach ( $field['value'] as $name => $value ) {
-								$values .= sprintf(
-									'<li>%s: %s</li>',
-									esc_html( $name ),
-									esc_html( $value )
-								);
+								$values .= sprintf( '<li>%s: %s</li>', esc_html( $name ), esc_html( $value ) );
 							}
+
 							$values .= '</ul>';
 						} else {
 							$values = esc_html( $field['value'] );
 						}
 
-						printf(
-							'<tr><td>%s</td><td>%s</td></tr>',
-							esc_html( $field['label'] ),
-							$values
-						);
+						if ( in_array( $field_name, $sizes_fields, true ) ) {
+							printf( '<tr><td>%s</td><td class="%s">%s</td></tr>', esc_html( $field['label'] ), esc_attr( $field_name ), $values );
+						} else {
+							printf( '<tr><td>%s</td><td>%s</td></tr>', esc_html( $field['label'] ), $values );
+						}
 					}
+
 					?>
 					</tbody>
 				</table>
