diff --git src/wp-includes/general-template.php src/wp-includes/general-template.php
index 23b2f1c..a5fa3ff 100644
--- src/wp-includes/general-template.php
+++ src/wp-includes/general-template.php
@@ -2820,6 +2820,7 @@ function wp_resource_hints() {
 		'preconnect'   => array(),
 		'prefetch'     => array(),
 		'prerender'    => array(),
+		'preload'      => array(),
 	);
 
 	/*
@@ -2830,6 +2831,8 @@ function wp_resource_hints() {
 	$hints['dns-prefetch'][] = apply_filters( 'emoji_svg_url', 'https://s.w.org/images/core/emoji/2.2.1/svg/' );
 
 	foreach ( $hints as $relation_type => $urls ) {
+		$unique_urls = array();
+
 		/**
 		 * Filters domains and URLs for resource hints of relation type.
 		 *
@@ -2841,16 +2844,31 @@ function wp_resource_hints() {
 		$urls = apply_filters( 'wp_resource_hints', $urls, $relation_type );
 
 		foreach ( $urls as $key => $url ) {
+			$atts = array();
+
+			if ( is_array( $url ) ) {
+				if ( isset( $url['href'] ) ) {
+					$atts = $url;
+					$url  = $url['href'];
+				} else {
+					continue;
+				}
+			}
+
 			$url = esc_url( $url, array( 'http', 'https' ) );
+
 			if ( ! $url ) {
-				unset( $urls[ $key ] );
+				continue;
+			}
+
+			if ( isset( $unique_urls[ $url ] ) ) {
 				continue;
 			}
 
 			if ( in_array( $relation_type, array( 'preconnect', 'dns-prefetch' ) ) ) {
 				$parsed = wp_parse_url( $url );
+
 				if ( empty( $parsed['host'] ) ) {
-					unset( $urls[ $key ] );
 					continue;
 				}
 
@@ -2862,13 +2880,34 @@ function wp_resource_hints() {
 				}
 			}
 
-			$urls[ $key ] = $url;
+			$atts['rel'] = $relation_type;
+			$atts['href'] = $url;
+
+			$unique_urls[ $url ] = $atts;
 		}
 
-		$urls = array_unique( $urls );
+		foreach ( $unique_urls as $atts ) {
+			$html = '';
+
+			foreach ( $atts as $attr => $value ) {
+				if ( ! is_scalar( $value ) ||
+				     ( ! in_array( $attr, array( 'as', 'crossorigin', 'href', 'pr', 'rel', 'type' ), true ) && ! is_numeric( $attr ))
+				) {
+					continue;
+				}
+
+				$value = ( 'href' === $attr ) ? esc_url( $value ) : esc_attr( $value );
+
+				if ( ! is_string( $attr ) ) {
+					$html .= " $value";
+				} else {
+					$html .= " $attr='$value'";
+				}
+			}
+
+			$html = trim( $html );
 
-		foreach ( $urls as $url ) {
-			printf( "<link rel='%s' href='%s' />\n", $relation_type, $url );
+			echo "<link $html />\n";
 		}
 	}
 }
diff --git tests/phpunit/tests/general/resourceHints.php tests/phpunit/tests/general/resourceHints.php
index a25d8bb..71b0d69 100644
--- tests/phpunit/tests/general/resourceHints.php
+++ tests/phpunit/tests/general/resourceHints.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * @group  template
+ * @group template
  * @ticket 34292
  */
 class Tests_WP_Resource_Hints extends WP_UnitTestCase {
@@ -242,4 +242,95 @@ class Tests_WP_Resource_Hints extends WP_UnitTestCase {
 
 		return $hints;
 	}
+
+	/**
+	 * @group 38121
+	 */
+	function test_preload() {
+		$expected = "<link rel='dns-prefetch' href='//s.w.org' />\n" .
+		            "<link rel='preload' href='https://make.wordpress.org/great-again' />\n" .
+		            "<link rel='preload' href='http://jobs.wordpress.net' />\n" .
+		            "<link rel='preload' href='//core.trac.wordpress.org' />\n";
+
+		add_filter( 'wp_resource_hints', array( $this, '_add_preload_urls' ), 10, 2 );
+
+		$actual = get_echo( 'wp_resource_hints' );
+
+		remove_filter( 'wp_resource_hints', array( $this, '_add_preload_urls' ) );
+
+		$this->assertEquals( $expected, $actual );
+	}
+
+	function _add_preload_urls( $hints, $method ) {
+		if ( 'preload' === $method ) {
+			$hints[] = 'https://make.wordpress.org/great-again';
+			$hints[] = 'http://jobs.wordpress.net';
+			$hints[] = '//core.trac.wordpress.org';
+			$hints[] = 'htps://wordpress.org'; // Invalid URLs should be skipped.
+		}
+
+		return $hints;
+	}
+
+	/**
+	 * @group 38121
+	 */
+	function test_custom_attributes() {
+		$expected = "<link rel='dns-prefetch' href='//s.w.org' />\n" .
+		            "<link rel='preconnect' href='https://make.wordpress.org' />\n" .
+		            "<link pr='0.5' href='http://wordpress.org' rel='prefetch' />\n" .
+		            "<link href='http://wordpress.org' rel='prerender' />\n" .
+		            "<link crossorigin as='font' type='font/woff2' href='https://example.com/font.woff2' rel='preload' />\n" .
+		            "<link crossorigin='use-credentials' as='style' href='https://example.com/foo.css' rel='preload' />\n";
+
+		add_filter( 'wp_resource_hints', array( $this, '_add_url_with_attributes' ), 10, 2 );
+
+		$actual = get_echo( 'wp_resource_hints' );
+
+		remove_filter( 'wp_resource_hints', array( $this, '_add_url_with_attributes' ) );
+
+		$this->assertEquals( $expected, $actual );
+	}
+
+	function _add_url_with_attributes( $hints, $method ) {
+		// Ignore hints with missing href attributes.
+		$hints[] = array(
+			'rel'  => 'foo',
+		);
+
+		if ( 'preconnect' === $method ) {
+			// Should ignore rel attributes.
+			$hints[] = array(
+				'rel'  => 'foo',
+				'href' => 'https://make.wordpress.org/great-again',
+			);
+		} elseif ( 'preload' === $method ) {
+			// Should support typical preload argument.
+			$hints[] = array(
+				'crossorigin',
+				'as'   => 'font',
+				'type' => 'font/woff2',
+				'href' => 'https://example.com/font.woff2',
+			);
+			$hints[] = array(
+				'crossorigin' => 'use-credentials',
+				'as'          => 'style',
+				'href'        => 'https://example.com/foo.css',
+			);
+		} elseif ( 'prefetch' === $method ) {
+			$hints[] = array(
+				'pr'   => 0.5,
+				'href' => 'http://wordpress.org',
+			);
+		} elseif ( 'prerender' === $method ) {
+			// Ignore invalid attributes.
+			$hints[] = array(
+				'foo'  => 'bar',
+				'bar'  => 'baz',
+				'href' => 'http://wordpress.org',
+			);
+		}
+
+		return $hints;
+	}
 }
