WordPress blocks SVG uploads by default and shows “Sorry, you are not allowed to upload this file type.” That is deliberate: an SVG file is XML that can carry JavaScript, so anyone who can upload one can place a script on your domain.
This snippet lets administrators upload SVG logos and icons through the Media Library and the block editor, and keeps the block in place for every other role.
The snippet
<?php
/**
* Only administrators who may already post unfiltered HTML can upload SVG.
*/
function dpc_can_upload_svg( $user = null ) {
if ( null === $user ) {
$user = wp_get_current_user();
}
return user_can( $user, 'manage_options' ) && user_can( $user, 'unfiltered_html' );
}
add_filter( 'upload_mimes', 'dpc_svg_upload_mimes', 10, 2 );
function dpc_svg_upload_mimes( $mimes, $user = null ) {
if ( dpc_can_upload_svg( $user ) ) {
$mimes['svg'] = 'image/svg+xml';
}
return $mimes;
}
add_filter( 'wp_check_filetype_and_ext', 'dpc_svg_check_filetype', 10, 3 );
/**
* Some servers detect SVG as text/plain or text/html, so WordPress rejects it.
* Accept the file only when it is named .svg and really contains SVG markup.
*/
function dpc_svg_check_filetype( $data, $file, $filename ) {
if ( ! empty( $data['ext'] ) || ! dpc_can_upload_svg() ) {
return $data;
}
if ( 'svg' !== strtolower( pathinfo( $filename, PATHINFO_EXTENSION ) ) || ! is_readable( $file ) ) {
return $data;
}
$head = file_get_contents( $file, false, null, 0, 4096 );
if ( false === $head || false === stripos( $head, '<svg' ) ) {
return $data;
}
$data['ext'] = 'svg';
$data['type'] = 'image/svg+xml';
return $data;
}
Add it with Scripts Organizer
Scripts Organizer ships this snippet in its One-Click Import library. To add it by hand:
- In wp-admin go to Scripts Organizer → Code Blocks → Add New and give the block a title.
- Script location: PHP.
- Trigger location: Everywhere. Not Admin only: the block editor uploads through the REST API (
/wp-json/wp/v2/media), which is not a wp-admin request, so Admin only code would not run there. - Action hook: leave it empty. The code only registers filters and actions, so it can run while plugins load.
- Paste the snippet into the PHP editor, enable the block and publish it.
The same code also works unchanged in a small plugin or an mu-plugin file in wp-content/mu-plugins/.
How it works
dpc_can_upload_svg()requires bothmanage_optionsandunfiltered_html. On a single site that means administrators. On multisite only super admins haveunfiltered_html, so site admins stay blocked. A user withunfiltered_htmlcan already publish raw<script>tags, so SVG uploads give them nothing new.upload_mimesaddssvgto the allowed file types. WordPress passes the user being checked as the second argument, and the snippet checks that user.wp_check_filetype_and_extcovers servers whose file detection reports an SVG astext/plainortext/html, which makes WordPress reject it even for admins. The filter only steps in when the name ends in.svgand the first 4 KB of the file contain<svg.- All capability checks run inside the filters. Scripts Organizer loads Everywhere code while plugins are still loading, before the current user is known, so calling
current_user_can()at the top level would not work. - Older copies of this snippet checked the
administratorrole name and read$_FILESon thepre-upload-uihook. A role name is not a capability, and that hook only prints the uploader, so both are gone.
Customise it
To let editors upload SVG as well, drop the manage_options check in dpc_can_upload_svg(). On a single site editors also have unfiltered_html, so you are not giving them a new kind of access, but every extra account that can upload SVG is one more account worth protecting.
Gotchas
- This snippet does not sanitise the file. A malicious SVG uploaded by an admin account, or by someone who took one over, still runs its script when a visitor opens the file URL directly or the SVG is embedded inline.
- If SVGs come from clients, designers or downloads, clean them before they reach the Media Library: run them through a sanitiser such as the open-source svg-sanitizer library on
wp_handle_upload_prefilter, or open the file and remove<script>elements,on…attributes and external references. - Browsers do not run scripts in an SVG loaded through an
<img>tag, so the Image block itself is not the risk. The risk is the direct file URL and inline or<object>embeds. - WordPress does not create thumbnail sizes for SVG and stores no width or height. Set a size on the Image block or in CSS.
- For another small upload filter, see Convert WordPress Image Filename To Lowercase.
In the Scripts Organizer library Import it with one click from Scripts Organizer.