diff --git src/js/_enqueues/admin/media.js src/js/_enqueues/admin/media.js
index 4d9a5ec46b..80a420d077 100644
--- src/js/_enqueues/admin/media.js
+++ src/js/_enqueues/admin/media.js
@@ -204,4 +204,40 @@
 			$( this ).find( '.found-radio input' ).prop( 'checked', true );
 		});
 	});
+
+	/**
+	 * Handle request to create missing image sizes.
+	 */
+	$( document ).ready( function() {
+		$( document ).on( 'click.wp-update-image-sizes', function( event ) {
+			var $target = $( event.target );
+
+			if ( $target.is( 'button.wp-update-image-sizes' ) ) {
+				var $parent = $target.parent();
+				var $message = $parent.find( 'span.message' );
+				var $spinner = $parent.find( 'span.spinner' );
+
+				$spinner.addClass( 'is-active' );
+
+				$.ajax({
+					type: 'post',
+					url: window.ajaxurl,
+					dataType: 'json',
+					data: {
+						action: 'wp-update-image-sizes',
+						update_sizes_nonce: $target.attr( 'data-wp-nonce' ),
+						attachment_id: $target.attr( 'data-wp-attachment-id' )
+					}
+				}).always( function() {
+					$spinner.removeClass( 'is-active' );
+				}).done( function( response ) {
+					if ( response.data && response.data.message ) {
+						$message.text( response.data.message );
+					}
+				}).fail( function() {
+					$message.text( attachMediaBoxL10n.error );
+				});
+			}
+		} );
+	} );
 })( jQuery );
diff --git src/wp-admin/admin-ajax.php src/wp-admin/admin-ajax.php
index 8d412dd9b8..59846557e2 100644
--- src/wp-admin/admin-ajax.php
+++ src/wp-admin/admin-ajax.php
@@ -131,6 +131,7 @@ $core_actions_post = array(
 	'edit-theme-plugin-file',
 	'wp-privacy-export-personal-data',
 	'wp-privacy-erase-personal-data',
+	'wp-update-image-sizes',
 	'health-check-site-status-result',
 	'health-check-dotorg-communication',
 	'health-check-is-in-debug-mode',
diff --git src/wp-admin/css/list-tables.css src/wp-admin/css/list-tables.css
index 91b1a2c9ce..598f2fbc6f 100644
--- src/wp-admin/css/list-tables.css
+++ src/wp-admin/css/list-tables.css
@@ -369,7 +369,8 @@ table.media .column-title .media-icon img {
 	vertical-align: top; /* Remove descender white-space. */
 }
 
-table.media .column-title .has-media-icon ~ .row-actions {
+table.media .column-title .has-media-icon ~ .row-actions,
+table.media .column-title .has-media-icon ~ .update-image-sizes {
 	margin-left: 70px; /* 60px image + margin */
 }
 
@@ -377,6 +378,11 @@ table.media .column-title .filename {
 	margin-bottom: 0.2em;
 }
 
+table.media .update-image-sizes .spinner {
+	float: none;
+	margin: 2px 10px;
+}
+
 /* @todo: pick a consistent list table selector */
 .wp-list-table a {
 	transition: none;
diff --git src/wp-admin/includes/ajax-actions.php src/wp-admin/includes/ajax-actions.php
index 0f1f0f4e04..18d7215e0e 100644
--- src/wp-admin/includes/ajax-actions.php
+++ src/wp-admin/includes/ajax-actions.php
@@ -5011,3 +5011,31 @@ function wp_ajax_health_check_get_sizes() {
 
 	wp_send_json_success( $all_sizes );
 }
+
+/**
+ * Ajax handler for updating image sizes.
+ *
+ * @since 5.0.0
+ */
+function wp_ajax_wp_update_image_sizes() {
+	if ( empty( $_POST['attachment_id'] ) ) {
+		wp_send_json_error( __( 'Missing attachment ID.' ) );
+	}
+
+	$attachment_id = (int) $_POST['attachment_id'];
+
+	if ( ! current_user_can( 'edit_post', $attachment_id ) || ! wp_verify_nonce( $_POST['update_sizes_nonce'], 'update_image_sizes' . $attachment_id ) ) {
+		wp_send_json_error( __( 'Invalid request.' ) );
+	}
+
+	$result = wp_update_image_attachment_sizes( $attachment_id );
+
+	if ( false === $result ) {
+		$response = array( 'message' => __( 'New image sub-sizes were not created. All sub-sizes already exist.' ) );
+	} else {
+		$count = count( $result );
+		$response = array( 'message' => sprintf( _n( 'Created %d image sub-size.', 'Created %d image sub-sizes.', $count ), $count ) );
+	}
+
+	wp_send_json_success( $response );
+}
diff --git src/wp-admin/includes/class-wp-media-list-table.php src/wp-admin/includes/class-wp-media-list-table.php
index 1a9970d9f4..ef9c28f261 100644
--- src/wp-admin/includes/class-wp-media-list-table.php
+++ src/wp-admin/includes/class-wp-media-list-table.php
@@ -411,6 +411,24 @@ class WP_Media_List_Table extends WP_List_Table {
 			?>
 		</p>
 		<?php
+		$missing_sizes = wp_check_image_attachment_sizes( $post->ID );
+
+		if ( ! empty( $missing_sizes ) ) {
+			$nonce = wp_create_nonce( 'update_image_sizes' . $post->ID );
+			$count = count( $missing_sizes );
+
+			?>
+			<p class="update-image-sizes">
+				<span class="message">
+					<?php printf( _n( '%s image sub-size file is missing.', '%s image sub-size files are missing.', $count ), $count ); ?>
+				</span>
+				<button type="button" class="button button-small wp-update-image-sizes" data-wp-nonce="<?php echo $nonce; ?>" data-wp-attachment-id="<?php echo $post->ID; ?>">
+				<?php _e( 'Refresh image sizes' ); ?>
+				</button>
+				<span class="spinner"></span>
+			</p>
+			<?php
+		}
 	}
 
 	/**
diff --git src/wp-admin/includes/image.php src/wp-admin/includes/image.php
index d0a2954e5f..71d3f16734 100644
--- src/wp-admin/includes/image.php
+++ src/wp-admin/includes/image.php
@@ -66,8 +66,199 @@ function wp_crop_image( $src, $src_x, $src_y, $src_w, $src_h, $dst_w, $dst_h, $s
 	return $dst_file;
 }
 
+function wp_check_image_attachment_sizes( $attachment_id ) {
+	if ( ! wp_attachment_is_image( $attachment_id ) ) {
+		return false;
+	}
+
+	$image_meta = wp_get_attachment_metadata( $attachment_id );
+	$defined_sizes = _wp_get_intermediate_image_sizes_with_data();
+
+	// Skip sizes that are too large.
+	foreach ( $defined_sizes as $size_name => $size_data ) {
+		$width = ! empty( $size_data['width'] ) ? (int) $size_data['width'] : 0;
+		$height = ! empty( $size_data['height'] ) ? (int) $size_data['height'] : 0;
+
+		if ( $width && $height ) {
+			if ( $width > $image_meta['width'] && $height > $image_meta['height'] ) {
+				unset( $defined_sizes[ $size_name ] );
+			}
+		} elseif ( ! $width ) {
+			if ( $height > $image_meta['height'] ) {
+				unset( $defined_sizes[ $size_name ] );
+			}
+		} elseif ( ! $height ) {
+			if ( $width > $image_meta['width'] ) {
+				unset( $defined_sizes[ $size_name ] );
+			}
+		}
+	}
+
+	if ( empty( $image_meta['sizes'] ) ) {
+		$image_meta['sizes'] = array();
+	}
+
+	$missing_sizes = array_diff( array_keys( $defined_sizes ), array_keys( $image_meta['sizes'] ) );
+
+	/**
+	 * Filters the missing image size files per attachment.
+	 *
+	 * @since 5.0.0
+	 *
+	 * @param array $missing_sizes Array with the missing image sizes.
+	 * @param array $image_meta The image attachment meta data.
+	 * @param string $attachment_id The attachment post ID.
+	 */
+	return apply_filters( 'wp_check_image_attachment_sizes', $missing_sizes, $image_meta, $attachment_id );
+}
+
+/**
+ * For an image attachment, if any of the currently defined image sub-sizes are missing,
+ * creates them and updates the image meta data.
+ *
+ * @since 5.0
+ *
+ * @param int $attachment_id The image attachment post ID.
+ * @return bool Whether any sizes were added.
+ */
+function wp_update_image_attachment_sizes( $attachment_id ) {
+	$missing_sizes = wp_check_image_attachment_sizes( $attachment_id );
+
+	if ( ! empty( $missing_sizes ) ) {
+		$file = get_attached_file( $attachment_id );
+		$image_meta = wp_get_attachment_metadata( $attachment_id );
+		$new_meta = _wp_create_image_subsizes( $file, $image_meta, $attachment_id );
+
+		$updated_sizes = array_diff_assoc( $new_meta['sizes'], $image_meta['sizes'] );
+
+		if ( ! empty( $updated_sizes ) ) {
+			return $updated_sizes;
+		}
+	}
+
+	return false;
+}
+
+function _wp_get_intermediate_image_sizes_with_data() {
+	$_wp_additional_image_sizes = wp_get_additional_image_sizes();
+	$sizes = array();
+
+	foreach ( get_intermediate_image_sizes() as $size ) {
+		$size_data = array(
+			'width'  => '',
+			'height' => '',
+			'crop'   => false,
+		);
+
+		if ( isset( $_wp_additional_image_sizes[ $size ]['width'] ) ) {
+			// For theme-added sizes
+			$size_data['width'] = intval( $_wp_additional_image_sizes[ $size ]['width'] );
+		} else {
+			// For default sizes set in options
+			$size_data['width'] = get_option( "{$size}_size_w" );
+		}
+
+		if ( isset( $_wp_additional_image_sizes[ $size ]['height'] ) ) {
+			// For theme-added sizes
+			$size_data['height'] = intval( $_wp_additional_image_sizes[ $size ]['height'] );
+		} else {
+			// For default sizes set in options
+			$size_data['height'] = get_option( "{$size}_size_h" );
+		}
+
+		if ( isset( $_wp_additional_image_sizes[ $size ]['crop'] ) ) {
+			// For theme-added sizes
+			$size_data['crop'] = $_wp_additional_image_sizes[ $size ]['crop'];
+		} else {
+			// For default sizes set in options
+			$size_data['crop'] = get_option( "{$size}_crop" );
+		}
+
+		if ( empty( $size_data['width'] ) && empty( $size_data['height'] ) ) {
+			continue;
+		}
+
+		$sizes[ $size ] = $size_data;
+	}
+
+	return $sizes;
+}
+
 /**
- * Generate post thumbnail attachment meta data.
+ * Create image sub-sizes and add the related data to the image meta `sizes` array.
+ *
+ * Saves/updates the image meta after each sub-size is created.
+ *
+ * @since 5.0
+ *
+ * @param string $file Full filepath of the attached image.
+ * @param array $image_meta The attachment meta data.
+ * @param int $attachment_id Attachment Id to process.
+ * @return array The attachment meta data with updated `sizes` array.
+ */
+function _wp_create_image_subsizes( $file, $image_meta, $attachment_id ) {
+	$new_sizes = _wp_get_intermediate_image_sizes_with_data();
+	/**
+	 * Filters the image sizes automatically generated when uploading an image.
+	 *
+	 * @since 2.9.0
+	 * @since 4.4.0 Added the `$metadata` argument.
+	 * @since 5.0.0 Added the `$attachment_id` argument.
+	 *
+	 * @param array $sizes         An associative array of image sizes.
+	 * @param array $metadata      An associative array of image metadata: width, height, file.
+	 * @param int   $attachment_id Current attachment ID.
+	 */
+	$new_sizes = apply_filters( 'intermediate_image_sizes_advanced', $new_sizes, $image_meta, $attachment_id );
+
+	// Check if any of the sizes already exist.
+	if ( ! empty( $image_meta['sizes'] ) && is_array( $image_meta['sizes'] ) ) {
+		foreach ( $image_meta['sizes'] as $size_name => $size_data ) {
+			if ( array_key_exists( $size_name, $new_sizes ) ) {
+				// Can check here if file exists..?
+				unset( $new_sizes[ $size_name ] );
+			} else {
+				// Make sure we are not adding the exact same file under different "size name".
+				foreach ( $new_sizes as $new_size_name => $new_size_data ) {
+					if ( $size_data['width'] === $new_size_data['width'] || $size_data['height'] === $new_size_data['height'] ) {
+						// $new_size_data['width'] or $new_size_data['height'] may be empty (meaning the new image size has either max-width or max-height),
+						// or they may be set to max-width and max-height like the default "large" size (1024px width and 1024px height).
+						$check = wp_constrain_dimensions( $size_data['width'], $size_data['height'], $new_size_data['width'], $new_size_data['height'] );
+
+						if ( $check[0] === $size_data['width'] && $check[1] === $size_data['height'] ) {
+							unset( $new_sizes[ $new_size_name ] );
+						}
+					}
+				}
+			}
+		}
+	}
+
+	if ( ! empty( $new_sizes ) ) {
+		$editor = wp_get_image_editor( $file );
+
+		if ( ! is_wp_error( $editor ) ) {
+			if ( empty( $image_meta['sizes'] ) ) {
+				$image_meta['sizes'] = array();
+			}
+
+			foreach ( $new_sizes as $new_size_name => $new_size_data ) {
+				$size_meta = $editor->make_subsize( $new_size_data );
+
+				if ( ! is_wp_error( $size_meta ) ) {
+					// The sub-size was created successfully. Save the size meta value.
+					$image_meta['sizes'][ $new_size_name ] = $size_meta;
+					wp_update_attachment_metadata( $attachment_id, $image_meta );
+				}
+			}
+		}
+	}
+
+	return $image_meta;
+}
+
+/**
+ * Generate attachment meta data and create image sub-sizes for images.
  *
  * @since 2.1.0
  *
@@ -153,6 +344,9 @@ function wp_generate_attachment_metadata( $attachment_id, $file ) {
 		if ( $image_meta ) {
 			$metadata['image_meta'] = $image_meta;
 		}
+
+		// Make thumbnails and other intermediate sizes.
+		$metadata = _wp_create_image_subsizes( $file, $metadata, $attachment_id );
 	} elseif ( wp_attachment_is( 'video', $attachment ) ) {
 		$metadata = wp_read_video_metadata( $file );
 		$support  = current_theme_supports( 'post-thumbnails', 'attachment:video' ) || post_type_supports( 'attachment:video', 'thumbnail' );
diff --git src/wp-includes/class-wp-image-editor-gd.php src/wp-includes/class-wp-image-editor-gd.php
index 72fb04ee72..dfaaba97c4 100644
--- src/wp-includes/class-wp-image-editor-gd.php
+++ src/wp-includes/class-wp-image-editor-gd.php
@@ -217,43 +217,75 @@ class WP_Image_Editor_GD extends WP_Image_Editor {
 	 * @return array An array of resized images' metadata by size.
 	 */
 	public function multi_resize( $sizes ) {
-		$metadata  = array();
-		$orig_size = $this->size;
+		$metadata = array();
 
 		foreach ( $sizes as $size => $size_data ) {
-			if ( ! isset( $size_data['width'] ) && ! isset( $size_data['height'] ) ) {
-				continue;
-			}
+			$meta = $this->make_subsize( $size_data );
 
-			if ( ! isset( $size_data['width'] ) ) {
-				$size_data['width'] = null;
-			}
-			if ( ! isset( $size_data['height'] ) ) {
-				$size_data['height'] = null;
+			if ( ! is_wp_error( $meta ) ) {
+				$metadata[ $size ] = $meta;
 			}
+		}
 
-			if ( ! isset( $size_data['crop'] ) ) {
-				$size_data['crop'] = false;
-			}
+		return $metadata;
+	}
+
+	/**
+	 * Create an image sub-size and return the image meta data value for it.
+	 *
+	 * @since 5.0
+	 *
+	 * @param array $size_data Array of height, width and whether to crop.
+	 * @return WP_Error|array WP_Error on error, or the image data array for inclusion in the `sizes` array in the image meta.
+	 */
+	public function make_subsize( $size_data ) {
+		if ( ! isset( $size_data['width'] ) && ! isset( $size_data['height'] ) ) {
+			return new WP_Error( 'image_subsize_create_error', __( 'Cannot resize the image. Both width and height are not set.' ) );
+		}
+
+		$orig_size  = $this->size;
+		$resized    = false;
 
-			$image     = $this->_resize( $size_data['width'], $size_data['height'], $size_data['crop'] );
-			$duplicate = ( ( $orig_size['width'] == $size_data['width'] ) && ( $orig_size['height'] == $size_data['height'] ) );
+		if ( ! isset( $size_data['width'] ) ) {
+			$size_data['width'] = null;
+		}
+
+		if ( ! isset( $size_data['height'] ) ) {
+			$size_data['height'] = null;
+		}
 
-			if ( ! is_wp_error( $image ) && ! $duplicate ) {
-				$resized = $this->_save( $image );
+		if ( ! isset( $size_data['crop'] ) ) {
+			$size_data['crop'] = false;
+		}
+
+		$image     = $this->_resize( $size_data['width'], $size_data['height'], $size_data['crop'] );
+		$duplicate = ( ( $orig_size['width'] == $size_data['width'] ) && ( $orig_size['height'] == $size_data['height'] ) );
+
+		if ( is_wp_error( $image ) ) {
+			return $image;
+		}
 
-				imagedestroy( $image );
+		if ( ! $duplicate ) {
+			$resized = $this->_save( $image );
 
-				if ( ! is_wp_error( $resized ) && $resized ) {
-					unset( $resized['path'] );
-					$metadata[ $size ] = $resized;
-				}
+			imagedestroy( $image );
+
+			if ( is_wp_error( $resized ) ) {
+				return $resized;
 			}
 
-			$this->size = $orig_size;
+			if ( $resized ) {
+				unset( $resized['path'] );
+			}
 		}
 
-		return $metadata;
+		$this->size = $orig_size;
+
+		if ( ! $resized ) {
+			return new WP_Error( 'image_subsize_create_error', __( 'Failed to create image sub-size.' ) );
+		}
+
+		return $resized;
 	}
 
 	/**
diff --git src/wp-includes/class-wp-image-editor-imagick.php src/wp-includes/class-wp-image-editor-imagick.php
index abb215038b..608d5f37d6 100644
--- src/wp-includes/class-wp-image-editor-imagick.php
+++ src/wp-includes/class-wp-image-editor-imagick.php
@@ -413,7 +413,7 @@ class WP_Image_Editor_Imagick extends WP_Image_Editor {
 	 * @since 3.5.0
 	 *
 	 * @param array $sizes {
-	 *     An array of image size arrays. Default sizes are 'small', 'medium', 'medium_large', 'large'.
+	 *     An array of image size arrays. Default sizes are 'thumbnail', 'medium', 'medium_large', 'large'.
 	 *
 	 *     Either a height or width must be provided.
 	 *     If one of the two is set to null, the resize will
@@ -430,52 +430,79 @@ class WP_Image_Editor_Imagick extends WP_Image_Editor {
 	 * @return array An array of resized images' metadata by size.
 	 */
 	public function multi_resize( $sizes ) {
-		$metadata   = array();
-		$orig_size  = $this->size;
-		$orig_image = $this->image->getImage();
+		$metadata = array();
 
 		foreach ( $sizes as $size => $size_data ) {
-			if ( ! $this->image ) {
-				$this->image = $orig_image->getImage();
-			}
+			$meta = $this->make_subsize( $size_data );
 
-			if ( ! isset( $size_data['width'] ) && ! isset( $size_data['height'] ) ) {
-				continue;
+			if ( ! is_wp_error( $meta ) ) {
+				$metadata[ $size ] = $meta;
 			}
+		}
 
-			if ( ! isset( $size_data['width'] ) ) {
-				$size_data['width'] = null;
-			}
-			if ( ! isset( $size_data['height'] ) ) {
-				$size_data['height'] = null;
-			}
+		return $metadata;
+	}
 
-			if ( ! isset( $size_data['crop'] ) ) {
-				$size_data['crop'] = false;
-			}
+	/**
+	 * Create an image sub-size and return the image meta data value for it.
+	 *
+	 * @since 5.0.0
+	 *
+	 * @param array $size_data Array of height, width and whether to crop.
+	 * @return WP_Error|array WP_Error on error, or the image data array for inclusion in the `sizes` array in the image meta.
+	 */
+	public function make_subsize( $size_data ) {
+		if ( ! isset( $size_data['width'] ) && ! isset( $size_data['height'] ) ) {
+			return new WP_Error( 'image_subsize_create_error', __( 'Cannot resize the image. Both width and height are not set.' ) );
+		}
+
+		$orig_size  = $this->size;
+		$orig_image = $this->image->getImage();
+		$resized    = false;
+
+		if ( ! isset( $size_data['width'] ) ) {
+			$size_data['width'] = null;
+		}
+
+		if ( ! isset( $size_data['height'] ) ) {
+			$size_data['height'] = null;
+		}
 
-			$resize_result = $this->resize( $size_data['width'], $size_data['height'], $size_data['crop'] );
-			$duplicate     = ( ( $orig_size['width'] == $size_data['width'] ) && ( $orig_size['height'] == $size_data['height'] ) );
+		if ( ! isset( $size_data['crop'] ) ) {
+			$size_data['crop'] = false;
+		}
 
-			if ( ! is_wp_error( $resize_result ) && ! $duplicate ) {
-				$resized = $this->_save( $this->image );
+		$resize_result = $this->resize( $size_data['width'], $size_data['height'], $size_data['crop'] );
+		$duplicate     = ( ( $orig_size['width'] == $size_data['width'] ) && ( $orig_size['height'] == $size_data['height'] ) );
 
-				$this->image->clear();
-				$this->image->destroy();
-				$this->image = null;
+		if ( is_wp_error( $resize_result ) ) {
+			return $resize_result;
+		}
 
-				if ( ! is_wp_error( $resized ) && $resized ) {
-					unset( $resized['path'] );
-					$metadata[ $size ] = $resized;
-				}
+		if ( ! $duplicate ) {
+			$resized = $this->_save( $this->image );
+
+			$this->image->clear();
+			$this->image->destroy();
+			$this->image = null;
+
+			if ( is_wp_error( $resized ) ) {
+				return $resized;
 			}
 
-			$this->size = $orig_size;
+			if ( $resized ) {
+				unset( $resized['path'] );
+			}
 		}
 
-		$this->image = $orig_image;
+		$this->size = $orig_size;
+		$this->image = $orig_image->getImage();
 
-		return $metadata;
+		if ( ! $resized ) {
+			return new WP_Error( 'image_subsize_create_error', __( 'Failed to create image sub-size.' ) );
+		}
+
+		return $resized;
 	}
 
 	/**
