SEO в WordPress без плагинов

SEO в WordPress без плагинов

Плагины SEO оптимизации имеют ряд недостатков. Основные претензии к плагинам для SEO это некорректная работа с микроразметкой.

В большинстве случаев для поисковой оптимизации WordPress используются плагины Yoast и AIOSEO.

Оба этих плагина не лишены недостатков. И вроде-бы даже бесплатные версии этих плагинов прекрасно справляются с задачей поисковой оптимизации, но есть некоторые проблемы, обойти которые с использованием плагинов для SEO не представляется возможным без глубокой переработки кода этих плагинов.

Среди недостатков плагинов для SEO я бы отметил следующие:

  • Не корректная работа с каноническими URL
  • Не корректная работа с микроразметкой
  • Не корректная работа с картой сайта
  • Часть функционала отнесена к платным версиям плагинов
  • Избыточная перегрузка кода
  • Дополнительная нагрузка на сервер


Я не буду детально разбирать все ошибки, возникающие при работе SEO плагинов, но они есть и по итогам получается, что проще написать свою программную часть для поисковой оптимизации, чем вечно исправлять то, что написали сторонние разработчики.

Оговорюсь, что в большинстве случаев любой плагин WordPress для поисковой оптимизации будет справляться со своими функциями, но нет смысла отрицать проблемы.

Конечно, решение из коробки “поставил и забыл” вполне подходит для большинства сайтов, но почему бы не избавиться от лишнего и не использовать собственный код, который подходит идеально?

В “голом”, пустом WordPress вопросы поисковой оптимизации не решены никак, от слова совсем никак. Для SEO выводится только title и на этом функционал заканчивается.

Для того чтобы написать собственный модуль для поисковой оптимизации нам потребуется плагин для создания дополнительных полей Advanced Custom Fields.

плагин для создания дополнительных полей Advanced Custom Fields.


Плагин ACF по сути фреймворк разработчика с большим функционалом и годится не только для поисковой оптимизации, но и для других дополнений. Фактически плагин ACF позволяет расширять функционал WordPress безгранично.

Для начала нам нужно создать дочернюю тему WordPress. Дочерняя тема нужна для того, чтобы при обновлении темы не пришлось каждый раз редактировать файлы этой темы.

В папку с нашей дочерней темой дополнительно загружаем файл header.php и основная работа будет производиться в этом файле.

Создав дочернюю тему устанавливаем плагин ACF.

Использование плагина ACF делится на 3 этапа:

1. Создание дополнительных полей.
2. Установка сгенерированного кода в файл function.php
3. Вывод значения переменных в файле header.php

На самом деле это совсем не сложно и я предлагаю готовое решение.
Можно скачать файл настроек, установить плагин ACF, импортировать этот файл настроек и вставить код, представленный ниже в файлы function.php и header.php нашей дочерней темы.

Код для function.php:

if( function_exists('acf_add_local_field_group') ):

acf_add_local_field_group(array(
	'key' => 'group_60747be41428b',
	'title' => 'SEO',
	'fields' => array(
		array(
			'key' => 'field_6074a2bed789e',
			'label' => 'Title',
			'name' => 'mytitle',
			'type' => 'text',
			'instructions' => '',
			'required' => 1,
			'conditional_logic' => 0,
			'wrapper' => array(
				'width' => '',
				'class' => '',
				'id' => '',
			),
			'default_value' => '',
			'placeholder' => '',
			'prepend' => '',
			'append' => 'Title страницы',
			'maxlength' => 65,
		),
		array(
			'key' => 'field_607479a0021eb',
			'label' => 'Description',
			'name' => 'description',
			'type' => 'textarea',
			'instructions' => '',
			'required' => 1,
			'conditional_logic' => 0,
			'wrapper' => array(
				'width' => '',
				'class' => '',
				'id' => '',
			),
			'default_value' => '',
			'placeholder' => '',
			'maxlength' => 255,
			'rows' => 6,
			'new_lines' => '',
		),
		array(
			'key' => 'field_607606978b9da',
			'label' => 'Robots',
			'name' => 'myrobots',
			'type' => 'button_group',
			'instructions' => 'Noindex для страницы или записи',
			'required' => 1,
			'conditional_logic' => 0,
			'wrapper' => array(
				'width' => '',
				'class' => '',
				'id' => '',
			),
			'choices' => array(
				'index, follow' => 'index, follow',
				'noindex' => 'noindex',
				'nofollow' => 'nofollow',
				'noindex, nofollow' => 'noindex, nofollow',
			),
			'allow_null' => 0,
			'default_value' => 'index, follow',
			'layout' => 'horizontal',
			'return_format' => 'value',
		),
	),
	'location' => array(
		array(
			array(
				'param' => 'post_type',
				'operator' => '==',
				'value' => 'post',
			),
		),
		array(
			array(
				'param' => 'page',
				'operator' => '!=',
				'value' => '3',
			),
		),
	),
	'menu_order' => 0,
	'position' => 'normal',
	'style' => 'default',
	'label_placement' => 'top',
	'instruction_placement' => 'label',
	'hide_on_screen' => '',
	'active' => true,
	'description' => '',
));

endif;


Код для header.php:

<!-- если категория -->
<?php if (is_category()){ // вывод полей для категорий
if ( $cat_desc = strip_tags(category_description()) )
echo '<meta name="description" content="'.$cat_desc.'"/>'; // выводим description
echo '<meta name="og:description" content="'.$cat_desc.'"/>'."\n"; // выводим description для разметки OpenGraph
echo '<meta property="og:title" content="'.get_queried_object()->name.'"/>' ."\n"; // выводим title категории
echo '<meta property="og:url" content="http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'] .'"/>' ."\n"; // выводим канонический URL
echo '<meta property="og:image" content="http://ВАШ САЙТ.РУ/wp-content/uploads/defaut.png"/>' ."\n"; // выводим изображение по умолчанию
echo '<meta property="og:image:secure_url" content= "http://ВАШ САЙТ.РУ/wp-content/uploads/defaut.png"/>' ."\n"; // выводим изображение по умолчанию 
}	
?>
<!-- /если категория -->
<!-- это запись или страница -->
<?php
if ( is_page() || is_single()) {
$postid = $post->ID;
echo '<meta property="og:image" content="'.get_the_post_thumbnail_url($postid).'"/>' ."\n"; // выводим изображение записи или страницы
echo '<meta property="og:image:secure_url" content="'.get_the_post_thumbnail_url($postid).'"/>' ."\n"; // выводим изображение по умолчанию если сайт на https
{
if(get_field('description'))	
{
echo '<meta name="description" content="'.get_field('description').'"/>' ."\n"; // выводим description
echo '<meta property="og:description" content="'.get_field('description').'"/>' ."\n"; // выводим description для разметки OpenGraph
}	
if(get_field('mytitle'))
remove_action('wp_head', '_wp_render_title_tag',1);
{
echo '<title>' . get_field('mytitle') . '</title>' ."\n"; // выводим title
echo '<meta property="og:title" content="'.get_field('mytitle').'"/>' ."\n"; // выводим title для разметки OpenGraph
}
if(get_field('myrobots'))	
{
echo '<meta name="robots" content="'.get_field('myrobots').'" />' ."\n"; // выводим meta name="robots" 
}}}
?>
<!-- /это запись или страница -->
<meta property="og:type" content="article"/>
<meta name="twitter: card" content="summary"/>
<!-- рисуем канонический URL -->
<?php 
if (is_category() !=is_paged() || is_singular() ){
echo '<link rel="canonical" href="http://' .$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'].'"/>',PHP_EOL; // выводим канонический URL для остальных сущностей
}
?>
<!-- /рисуем канонический URL -->

<!--если архив меток или архив по датам или архив по авторам запрещаем индексировать -->
<?php
if( is_tag() || is_author() || is_date() ){
echo '<meta name="robots" content="noindex, nofollow"/>';
}
?>
<!-- /мета  -->
		
<!-- микроразметка schema.org-->
		<script type="application/ld+json">
{
    "@context": "http://schema.org",
    "@type": "Article",
    "mainEntityOfPage": {
        "@type": "WebPage",
        "@id": "<?php echo 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'].'';?>"
    },
    "headline": "<?php if(get_field('mytitle')) echo get_field('mytitle');?>",
    "image": {
        "@type": "ImageObject",
        "url": "<?php $postid = $post->ID; echo get_the_post_thumbnail_url($postid) ;?>",
        "width": 1280,
        "height": 800
    },
    "datePublished": "<?php echo get_the_date('c');?>",
    "dateModified": "<?php echo get_the_modified_date('c');?>",
    "author": {
        "@type": "Person",
        "name": "Дмитрий Дмитриев"
    },
    "publisher": {
        "@type": "Organization",
        "name": "Дмитрий Дмитриев - персональный блог",
        "logo": {
            "@type": "ImageObject",
            "url": "http://ВАШ САЙТ.РУ/wp-content/uploads/logo.png",
            "width": 120,
            "height": 120
        }
    },
    "description": "<?php if(get_field('description')) echo get_field('description');?>"
}
</script>
<!-- /микроразметка schema.org -->	

В этом примере я намеренно разбил вывод SEO на фрагменты и снабдил обширными комментариями. Пример полностью рабочий и используется на этом сайте. Замените название ВАШ САЙТ.РУ на название вашего домена и используйте http или https в зависимости от того, работает ли ваш сайт по защищенному протоколу или нет.

Так же пути для файлов изображений у вас будут свои, как впрочем и размеры изображений.

Заметил следующее: Один из моих проектов интегрирован в Twitter через сервис dlvrit.com так, как я писал в статье Интеграция WordPress с соцсетями. Ссылка из Twitter идет с редиректа на домен dvir.it и имеет примерно такую конструкцию: https://example.com/category/postname.html?utm_source=dlvr.it&utm_medium=twitter. Это ни чем не вредит, но строка, содержащая глобальную переменную $_SERVER['REQUEST_URI'] в секции создания канонического URL отвечает на запрос буквально и рисует конструуцию вида rel="canonical" href=https://example.com/category/postname.html?utm_source=dlvr.it&utm_medium=twitter" что совершенно неприемлемо. Чтобы исправить недочет в качестве временной меры я использовал редирект на каноническую страницу. Буду наблюдать, если такие страницы будут появляться массово наверное стоит использовать для создания канонического URL функцию get_permalink() WordPress, а не глобальную переменную.

Чтобы избежать неприятного момента когда сторонний ресурс ссылается на нашу страницу с дополнительными параметрами в URL можно вывести канонический URL не через глобальную переменную, а получить напрямую из переменных WordPress:

Код для секции вывода канонического URL записей и страниц:

if (is_singular() ){
echo '<link rel="canonical" href="'.get_permalink().'"/>',PHP_EOL;
}

Код для секции вывода канонического URL категорий:

if(is_category()) {
$categories = get_the_category();
$category_id = $categories[0]->term_id;
echo '<link rel="canonical" href="'. get_category_link($category_id).'" />';
}

Пошаговая инструкция

  1. Установить плагин ACF
  2. Скачать файл настроек плагина ACF
  3. Импортировать настройки плагина ACF через интерфейс
  4. Внедрить php код указанный выше в function.php и header.php
Импорт настроек дополнительных полей

Преимущества:

  • Поисковая оптимизация WordPress без плагинов и платных решений
  • Гибкость настроек. Возможно внедрение любого типа meta информации и микроразметки в том числе для новостей и видео.
  • Микроразметка для локального бизнеса
  • 100% совместимость с любой темой WordPress
  • Отсутствие конфликтов между плагинами
  • 100% валидность микроразметки при проверке расширенных данных Яндекс и Google

После установки и настройки плагина интерфейс добавления постов и страниц будет выглядеть так:

интерфейс добавления постов и страниц
Дополнительно

Ок, мы внедрили собственный код для поисковой оптимизации, но это еще не все.
Для завершения процесса помещаем файл robots.txt примерно такого содержания в корень сайта:

User-agent: *   
Disallow: /cgi-bin         
Disallow: /?               
Disallow: /wp-              
Disallow: *?s=           
Disallow: *&s=              
Disallow: /search
Disallow: /author/
Disallow: /users/           
Disallow: */trackback      
Disallow: */page/
Disallow: */feed                      
Disallow: */embed           
Disallow: /xmlrpc.php
Allow: */uploads  



Файл robots.txt нет смысла описывать, это тема для отдельной статьи.

Что же касается файла карты sitemap.xml есть великое множество решений, от установки плагинов до использования внешних сервисов.
Ну и чтобы окончательно закрыть вопрос в том же файле header.php указываем пути для иконок сайта.

Предложенное решение безусловно не идеально, но близко к идеальному. Если кто-то доработает предложенный вариант буду весьма признателен.

Удачи!


Обнаружил интересный баг. Точнее это не баг, а скорее фича. Некоторые касдомные типы записей имеют в интерфейсе добавление SEO. Это довольно странно потому что в настройках ACF я указывал что дополнительные поля должны появляться только в интерфейсе создания страниц и записей.

Добавление SEO к сущностям WordPress

Пока не разбирался почему так. Возможно разработчики плагинов, создающих собственные типы записей указывают что новая сущность имеет идентификатор страницы или записи. Ну, по крайней мере это единственное разумное объяснение. В частности при создании новой карусели постов в плагине Smart Post Show в интерфейсе появлялись дополнительные поля, которые, конечно же, требовали обязательного заполнения в соответствии с их настройками.

Впрочем на общем функционале это никак не отражается.

Об авторе

Дмитрий Дмитриев

Дмитрий administrator

Привет, меня зовут Дмитрий и я занимаюсь созданием сайтов на Wordpress. Мне нравится моя работа, я могу сделать вам сайт на Wordpress или помочь с реализацией ваших замыслов на другой CMS.

4 комментария

LeoДата:5:30 дп - Авг 30, 2021

А как добавить поля для социальных сетей? В вашем коде этого нет, а в плагине есть

    ДмитрийДата:5:04 дп - Окт 29, 2021

    Можно добавить дополнительные поля для соцсетей по аналогии с основными метаполями и точно так же сделать их вывод в файле header.php. На мой взгляд в большинстве случаев достаточно если значение метаполей равны значению микроразметки для соцсетей.

daskelДата:9:31 пп - Окт 27, 2021

А зачем код для function.php? Мы ведь поля импортируем из файла, насколько я понимаю.

    ДмитрийДата:5:20 дп - Окт 29, 2021

    Но как WordPress узнает, что поля существуют? С помощью импорта мы лишь создаем поля, которые никак не отобразятся в интерфейсе если не добавить код.

Оставить ответ