Как в WordPress загрузить SVG
По умолчанию в WordPress нельзя загружать SVG в целях безопасности. Но что делать, когда очень надо? Решение есть!

О том, будет ли разрешена загрузка SVG в WordPress по умолчанию и почему разработчики этого избегают, читайте в тикете #24251 Reconsider SVG inclusion to get_allowed_mime_types.
Плагин ― включить загрузку svg можно также через плагин Safe SVG. Он кроме прочего очищает код загружаемого файла.
Смотрите также: Разрешаем загрузку запрещенных типов файлов
Шаг 1 - Включение SVG в список разрешенных для загрузки файлов
Для этого воспользуемся хуком upload_mimes, позволяющим изменять список доступных для загрузки файлов по MIME-типу.
add_filter( 'upload_mimes', 'svg_upload_allow' );
# Добавляет SVG в список разрешенных для загрузки файлов.
function svg_upload_allow( $mimes ) {
$mimes['svg'] = 'image/svg+xml';
return $mimes;
}
Иногда этого кода достаточно, но не для всех svg файлов. Разберемся почему так.
Реальный MIME тип svg файла, который в WP определятся php функцией finfo_file() (см. код wp_check_filetype_and_ext() ) может быть двух вариантов:
image/svg+xml— когда в коде svg есть xml заголовок.image/svg— когда в коде svg есть только тег <svg>, без каких либо заголовков.
Сюда нужно добавить оба эти варианта, но добавить можно только один, поэтому только этого кода недостаточно и нужен второй шаг.
Шаг 2 - Подмена mime типа SVG
Как описано выше предыдущего кода для решения проблемы недостаточно. Точнее, в результате проверки и несовпадения указанного MIME типа и реального (полученного самим WP) MIME тип будет просто обнулен. Вернуть его обратно нам поможет хук wp_check_filetype_and_ext.
add_filter( 'wp_check_filetype_and_ext', 'fix_svg_mime_type', 10, 5 );
# Исправление MIME типа для SVG файлов.
function fix_svg_mime_type( $data, $file, $filename, $mimes, $real_mime = '' ){
// WP 5.1 +
if( version_compare( $GLOBALS['wp_version'], '5.1.0', '>=' ) ){
$dosvg = in_array( $real_mime, [ 'image/svg', 'image/svg+xml' ] );
}
else {
$dosvg = ( '.svg' === strtolower( substr( $filename, -4 ) ) );
}
// mime тип был обнулен, поправим его
// а также проверим право пользователя
if( $dosvg ){
// разрешим
if( current_user_can('manage_options') ){
$data['ext'] = 'svg';
$data['type'] = 'image/svg+xml';
}
// запретим
else {
$data['ext'] = false;
$data['type'] = false;
}
}
return $data;
}
Если из кода выше убрать проверку на право администратора, то код станет небезопасным и потенциально открывает дыру в защите сайта.
В коде выше добавлена проверка на версию WP. С WP 5.1.0 благодаря тикету автора этого сайта в хуке появился параметр $real_mime. Он позволяет сделать более надежную проверку файла — на уровне определения MIME типа файла по его коду, а не расширению.
Также рекомендую ограничить размер загружаемого svg файла, например не более 50кб. SVG файлы как правило маленькие и большой размер может говорить о том, что в файле какой-либо нехороший код.
Кода выше достаточно, чтобы WоrdPress разрешил загружать SVG в медиабиблиотеку. Следующий шаг чисто визуальный.
Отображение SVG в медиабиблиотеке
После проделанных шагов SVG файлы будут отображаться как документы, а не изображения:
За разметку этих блоков с изображениями отвечает код, генерируемый функцией wp_print_media_templates() из файла wp-includes/media-template.php:
<div class="thumbnail">
<# if ( data.uploading ) { #>
<div class="media-progress-bar"><div style="width: {{ data.percent }}%"></div></div>
<# } else if ( 'image' === data.type && data.sizes ) { #>
<div class="centered">
<img src="{{ data.size.url }}" draggable="false" alt="" />
</div>
<# } else { #>
<div class="centered">
<# if ( data.image && data.image.src && data.image.src !== data.icon ) { #>
<img src="{{ data.image.src }}" class="thumbnail" draggable="false" alt="" />
<# } else if ( data.sizes && data.sizes.medium ) { #>
<img src="{{ data.sizes.medium.url }}" class="thumbnail" draggable="false" alt="" />
<# } else { #>
<img src="{{ data.icon }}" class="icon" draggable="false" alt="" />
<# } #>
</div>
<div class="filename">
<div>{{ data.filename }}</div>
</div>
<# } #>
</div>
Смысл кода в том, что он отобразит изображение, если
- у него есть свойство src (ссылка на изображение) и оно не равно ссылке на иконку документа (любой файл по сути документ);
- или если у изображения есть размер medium (svg файл вообще не кропается).
Файл SVG не имеет ни первого ни второго, поэтому нам нужно сымитировать самостоятельно один из этих вариантов и в этом нам поможет фильтр wp_prepare_attachment_for_js, используйте любой из вариантов по вкусу.
Вариант 1 - С выводом названия файла
add_filter( 'wp_prepare_attachment_for_js', 'show_svg_in_media_library' );
# Формирует данные для отображения SVG как изображения в медиабиблиотеке.
function show_svg_in_media_library( $response ) {
if ( $response['mime'] === 'image/svg+xml' ) {
// С выводом названия файла
$response['image'] = [
'src' => $response['url'],
];
}
return $response;
}
Вариант 2 - Без вывода названия файла
add_filter( 'wp_prepare_attachment_for_js', 'show_svg_in_media_library' );
# Формирует данные для отображения SVG как изображения в медиабиблиотеке.
function show_svg_in_media_library( $response ) {
if ( $response['mime'] === 'image/svg+xml' ) {
// Без вывода названия файла
$response['sizes'] = [
'medium' => [
'url' => $response['url'],
],
// при редактирования картинки
'full' => [
'url' => $response['url'],
],
];
}
return $response;
}
Готовый класс
GitHub<?php
/**
* Allow uploading svg webp file types
*
* @version 1.1
*/
final class Allow_SVG_Upload {
public static function init(): void {
// Allow file types to be uploaded
add_filter( 'upload_mimes', [ __CLASS__, 'upload_allow_types' ] );
add_filter( 'wp_check_filetype_and_ext', [ __CLASS__, 'fix_svg_mime_type' ], 10, 5 );
add_filter( 'wp_prepare_attachment_for_js', [ __CLASS__, 'show_svg_in_media_library' ] );
add_filter( 'wp_handle_sideload_prefilter', [ __CLASS__, 'check_upload_file_size' ] );
add_filter( 'wp_handle_upload_prefilter', [ __CLASS__, 'check_upload_file_size' ] );
add_filter( 'getimagesize_mimes_to_exts', [ __CLASS__, 'more_mimes_to_exts' ] );
add_filter( 'image_sideload_extensions', [ __CLASS__, 'more_image_sideload_extensions' ] );
}
public static function upload_allow_types( $mimes ){
if( current_user_can( 'upload_files' ) ){
$mimes['svg'] = 'image/svg+xml';
}
$mimes['webp'] = 'image/webp';
// deactivate existing
unset( $mimes['mp4a'] );
return $mimes;
}
public static function more_image_sideload_extensions( $allowed ){
$allowed[] = 'svg';
return $allowed;
}
public static function more_mimes_to_exts( $mime_to_ext ){
$mime_to_ext['image/webp'] = 'webp';
return $mime_to_ext;
}
public static function fix_svg_mime_type( $data, $file, $filename, $mimes, $real_mime = '' ){
// WP 5.1 +
if( version_compare( $GLOBALS['wp_version'], '5.1.0', '>=' ) ){
$dosvg = in_array( $real_mime, [ 'image/svg', 'image/svg+xml' ] );
}
else{
$dosvg = ( '.svg' === strtolower( substr( $filename, -4 ) ) );
}
// mime type was reset, let's fix it
// and also check file size and user permissions
if( $dosvg ){
if( current_user_can( 'upload_files' ) ){
$data['ext'] = 'svg';
$data['type'] = 'image/svg+xml';
}
// prohibit
else {
$data['ext'] = $type_and_ext['type'] = false;
}
}
return $data;
}
/**
* Generates data for displaying SVGs as images in the media library.
*/
public static function show_svg_in_media_library( $response ) {
if ( $response['mime'] === 'image/svg+xml' ) {
$response['sizes'] = [
'medium' => [
'url' => $response['url'],
],
// when editing a image
'full' => [
'url' => $response['url'],
],
];
}
return $response;
}
/**
* Limit the size of uploaded files by type
*/
public static function check_upload_file_size( $file ){
if( ! $file ){
return $file;
}
// for SVG
if( str_contains( ( $file['type'] ?? '' ), 'image/svg+xml' ) ){
$size_limit = 500 * 1024; // max size in KB
if( (int) $file['size'] > $size_limit ){
$file['error'] = sprintf(
__( 'ERROR: Размер этого типа файлов не может превышать %s', 'km' ),
size_format( $size_limit )
);
}
}
return $file;
}
}
