wp_add_crossorigin_attributes()WP 7.1.0

Adds crossorigin="anonymous" to HTML tags in the supplied HTML code.

The crossorigin="anonymous" attribute instructs the browser to load an external resource through CORS without credentials: cookies, HTTP authentication, or client certificates. This allows a resource from another domain to be used safely.

The attribute is added only to tags that load resources from another origin. If no external resources are found, the HTML is unchanged.

The following tags and attributes are processed:

  • <audio src>
  • <link href>
  • <script src>
  • <video src> and <video poster>
  • <source src> inside <audio> or <video>

For an external resource in <source>, the attribute is added to its parent <audio> or <video>. <img> tags are not processed.

If a tag already has a crossorigin attribute, the function does not change it.

URLs that begin with the result of site_url() or with / are considered internal.

The function does not parse URLs into protocol, domain, and port. Therefore:

  • a relative URL without a leading slash, such as assets/app.js, is considered external.
  • a URL such as //cdn.example.com/app.js, on the other hand, is considered internal.

WordPress uses the function when preparing block-editor HTML with cross-origin isolation. This makes it possible to load external resources in the CORS mode required for client-side image processing through SharedArrayBuffer.

No Hooks.

Returns

String.

  • string — the processed HTML.

Usage

wp_add_crossorigin_attributes( $html ): string;
$html(string) (required)
The HTML in which to add crossorigin attributes.

Examples

#1 Adding attributes to external resources

The crossorigin="anonymous" attribute will be added to both tags.

$html = '
	<link rel="stylesheet" href="https://cdn.example.com/style.css">
	<script src="https://cdn.example.com/app.js"></script>

	<script src="/foo/bar.js"></script>
	<script src="foo/bar.js"></script>
';

echo wp_add_crossorigin_attributes( $html );

Result:

<link crossorigin="anonymous" rel="stylesheet" href="https://cdn.example.com/style.css">
<script crossorigin="anonymous" src="https://cdn.example.com/app.js"></script>

<script src="/foo/bar.js"></script>
<script crossorigin="anonymous" src="foo/bar.js"></script>

#2 Processing a source element

The crossorigin="anonymous" attribute will be added to the <video> tag, not the nested <source>.

$html = '
	<video controls>
		<source src="https://cdn.example.com/video.mp4" type="video/mp4">
	</video>
';

echo wp_add_crossorigin_attributes( $html );

Changelog

Since 7.1.0 Introduced.

wp_add_crossorigin_attributes() code WP 7.1

function wp_add_crossorigin_attributes( string $html ): string {
	$site_url = site_url();

	$processor = new WP_HTML_Tag_Processor( $html );

	// See https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin.
	$cross_origin_tag_attributes = array(
		'AUDIO'  => array( 'src' ),
		'LINK'   => array( 'href' ),
		'SCRIPT' => array( 'src' ),
		'VIDEO'  => array( 'src', 'poster' ),
		'SOURCE' => array( 'src' ),
	);

	while ( $processor->next_tag() ) {
		$tag = $processor->get_tag();

		if ( ! isset( $cross_origin_tag_attributes[ $tag ] ) ) {
			continue;
		}
		$crossorigin = $processor->get_attribute( 'crossorigin' );
		if ( null !== $crossorigin ) {
			continue;
		}

		if ( 'AUDIO' === $tag || 'VIDEO' === $tag ) {
			$processor->set_bookmark( 'audio-video-parent' );
		}

		$processor->set_bookmark( 'resume' );

		$sought = false;

		$is_cross_origin = false;

		foreach ( $cross_origin_tag_attributes[ $tag ] as $attr ) {
			$url = $processor->get_attribute( $attr );
			if ( is_string( $url ) && ! str_starts_with( $url, $site_url ) && ! str_starts_with( $url, '/' ) ) {
				$is_cross_origin = true;
			}

			if ( $is_cross_origin ) {
				break;
			}
		}

		if ( $is_cross_origin ) {
			if ( 'SOURCE' === $tag ) {
				$sought = $processor->seek( 'audio-video-parent' );

				if ( $sought ) {
					$processor->set_attribute( 'crossorigin', 'anonymous' );
				}
			} else {
				$processor->set_attribute( 'crossorigin', 'anonymous' );
			}

			if ( $sought ) {
				$processor->seek( 'resume' );
				$processor->release_bookmark( 'audio-video-parent' );
			}
		}
	}

	return $processor->get_updated_html();
}