Sanitizeresw_svg_remove_remote_references

esw_svg_remove_remote_references

Decide whether Easy SVG strips absolute URLs, remote CSS and @import rules from SVGs. On by default, so a stored file cannot load anything from elsewhere.

Filter · Easy SVG · Status: Stable

An SVG can load things from other servers: an image, a stylesheet, a font. Every visitor's browser then contacts that server, which can log who looked at your page. Easy SVG removes those references by default. This filter lets you switch that off, or make sure it stays on.

Usage

apply_filters( 'esw_svg_remove_remote_references', bool $remove )
add_filter( 'esw_svg_remove_remote_references', '__return_false' );

When to use it

  • Your SVGs are infographics with clickable links (a href="https://..."). With the default, those links lose their href on upload.
  • A trusted import, for example a migration from another site, brings SVGs that load a web font or a bitmap from a CDN, and you want them stored as they are.
  • You run a hardened site and want to guarantee that no other plugin switches the removal off. Return true late.

Do not use it:

  • Because one file looks wrong. Embed the image or font into the file (or host it as a separate media item) instead of opening every upload to remote loading.
  • To change what the icon library keeps. Icons drop every href that does not start with # anyway, whatever this filter says.

Parameters

body
$removebool
Required

true (the default) removes remote references, false keeps them. The return value is cast to bool, so 0, '' and null count as false.

Return value

returnbool

true strips remote references from every SVG cleaned after this point, false keeps them.

Default

true. Easy SVG 5.0.0 introduced this; up to 4.1 remote references were kept. The bundled library itself defaults to false, so the plugin always sets the value explicitly.

What true removes:

WhereWhat happens
Any attribute whose value is an absolute URL (http:, https:, ftp:, file:) or protocol-relative (//host/...)The whole attribute is removed. This includes href on a and image.
Any attribute containing a remote url(...) or @importThe whole attribute is removed.
A style attribute with a remote reference hidden behind CSS escapes or comments, or in image-set()The whole attribute is removed.
Text inside a style elementRemote url(...) and image-set(...) tokens are cut out, and every @import rule is removed, local ones included. The rest of the CSS stays.

What stays: fragment references (#gradient, url(#clip)), root-relative paths (/wp-content/uploads/a.png), relative paths inside CSS (url(images/a.png)) and the data:image/png, gif and jpeg URIs the href check accepts. A relative href such as images/a.png is removed either way: the separate href check, which this filter does not control, only accepts fragments, paths starting with /, http(s) and those data: types.

Examples

Keep remote references everywhere

// Links and remote images in uploaded SVGs are kept from now on.
add_filter( 'esw_svg_remove_remote_references', '__return_false' );

Keep them only during a trusted import

The value is read each time a sanitizer is built, so you can switch it off for one job and back on afterwards.

/**
 * Import SVGs from a folder we trust, keeping their remote references.
 *
 * @param string[] $paths Absolute paths of SVG files.
 * @return int[] New attachment IDs.
 */
function myplugin_import_trusted_svgs( array $paths ) {
	require_once ABSPATH . 'wp-admin/includes/file.php';
	require_once ABSPATH . 'wp-admin/includes/media.php';
	require_once ABSPATH . 'wp-admin/includes/image.php';

	$ids = array();

	add_filter( 'esw_svg_remove_remote_references', '__return_false' );

	try {
		foreach ( $paths as $path ) {
			$tmp = wp_tempnam( basename( $path ) );
			if ( ! $tmp || ! copy( $path, $tmp ) ) {
				continue;
			}

			$id = media_handle_sideload(
				array(
					'name'     => basename( $path ),
					'tmp_name' => $tmp,
				),
				0
			);

			if ( is_wp_error( $id ) ) {
				wp_delete_file( $tmp );
				continue;
			}

			$ids[] = $id;
		}
	} finally {
		remove_filter( 'esw_svg_remove_remote_references', '__return_false' );
	}

	return $ids;
}

media_handle_sideload() runs the same check as the uploader (wp_handle_sideload_prefilter), and the new-attachment check runs right after it. Both build their sanitizer while your filter is active, so both keep the references. The finally block makes sure a failing file cannot leave the filter on for the rest of the request.

Make sure nobody switches it off

/**
 * Force remote reference removal, whatever other plugins return.
 */
function myplugin_force_remote_reference_removal() {
	add_filter( 'esw_svg_remove_remote_references', '__return_true', PHP_INT_MAX );
}
add_action( 'plugins_loaded', 'myplugin_force_remote_reference_removal' );

Stricter for your add-on only

easy_svg_sanitizer() returns a new instance on every call, so an add-on can tighten its own copy without touching the site setting.

$sanitizer = function_exists( 'easy_svg_sanitizer' ) ? easy_svg_sanitizer() : null;

if ( null !== $sanitizer ) {
	// Our own content never needs remote references, whatever the site allows.
	$sanitizer->removeRemoteReferences( true );
}

With false, an uploaded SVG can make the browser of every visitor, and of every administrator who opens the media library, request a URL on a server you do not control. That server learns IP addresses, browsers and which page was viewed, and can change what it returns at any time. Only turn this off when you trust every person who can upload SVGs.

How it works

easy_svg_sanitizer() applies the filter on every call (easy-svg.php, line 294) and passes the result to the library's removeRemoteReferences():

Because the filter is applied after cloning the legacy global, it always wins. A site snippet from the 4.x days that called $sanitizer->removeRemoteReferences( false ) on the global no longer has any effect; the library cannot tell a deliberate false from its own default, so the plugin decides through the filter alone.

Every path that cleans SVG calls easy_svg_sanitizer(): media uploads and sideloads, the wp_upload_bits check used by XML-RPC, the check on every new attachment, the icon library and add-ons. On the wp_upload_bits path a file is refused instead of cleaned, so with the default a file containing a remote reference is rejected there.

Pitfalls

  • Your own domain counts as remote. The check looks at the URL form, not the host. https://your-site.example/wp-content/uploads/a.png is removed just like a third-party URL. Use root-relative paths (/wp-content/...) if a file must reference your own media.
  • Links lose their target. a href="https://..." keeps the a element and its content but loses the href, so the link is dead.
  • Local @import goes too. Inside a style element every @import rule is removed, not only remote ones.
  • Old files are not re-checked. Changing the filter affects files cleaned afterwards. Files already stored stay as they are.
  • The legacy global is ignored for this setting. Configure it through the filter only.
  • Icons keep no external href either way. The icon hardener removes every href that does not start with # after sanitizing.