Реализация переноса SEO-данных (мета-теги, редиректы) при миграции
SEO-данные — критичная часть миграции. Потеря позиций в поисковых системах после переезда может занять месяцы для восстановления. Правильный перенос мета-тегов и настройка редиректов минимизируют потери.
Что входит в SEO-данные
- Meta title и meta description каждой страницы
- Open Graph теги (og:title, og:description, og:image)
- Canonical URL
- Robots директивы (noindex, nofollow)
- Alt-тексты изображений
- Schema.org разметка
- Hreflang для мультиязычных сайтов
- Breadcrumb разметка
- Существующие редиректы
Экспорт SEO-данных из WordPress (Yoast)
import mysql.connector
def export_yoast_seo_data(wp_db):
cursor = wp_db.cursor(dictionary=True)
cursor.execute("""
SELECT
p.ID,
p.post_name as slug,
p.post_type,
COALESCE(yoast_title.meta_value, p.post_title) as seo_title,
yoast_desc.meta_value as seo_description,
yoast_og_title.meta_value as og_title,
yoast_og_desc.meta_value as og_description,
yoast_robots.meta_value as robots,
yoast_canonical.meta_value as canonical
FROM wp_posts p
LEFT JOIN wp_postmeta yoast_title ON p.ID = yoast_title.post_id
AND yoast_title.meta_key = '_yoast_wpseo_title'
LEFT JOIN wp_postmeta yoast_desc ON p.ID = yoast_desc.post_id
AND yoast_desc.meta_key = '_yoast_wpseo_metadesc'
LEFT JOIN wp_postmeta yoast_og_title ON p.ID = yoast_og_title.post_id
AND yoast_og_title.meta_key = '_yoast_wpseo_opengraph-title'
LEFT JOIN wp_postmeta yoast_og_desc ON p.ID = yoast_og_desc.post_id
AND yoast_og_desc.meta_key = '_yoast_wpseo_opengraph-description'
LEFT JOIN wp_postmeta yoast_robots ON p.ID = yoast_robots.post_id
AND yoast_robots.meta_key = '_yoast_wpseo_meta-robots-noindex'
LEFT JOIN wp_postmeta yoast_canonical ON p.ID = yoast_canonical.post_id
AND yoast_canonical.meta_key = '_yoast_wpseo_canonical'
WHERE p.post_status = 'publish'
AND p.post_type IN ('post', 'page')
""")
return cursor.fetchall()
Экспорт существующих редиректов из Yoast/Redirection
def export_existing_redirects(wp_db):
cursor = wp_db.cursor(dictionary=True)
# Плагин Yoast Premium хранит редиректы в таблице wp_yoast_seo_links
# Плагин Redirection — в wp_redirection_items
cursor.execute("""
SELECT url as source, action_data as destination, action_code as code
FROM wp_redirection_items
WHERE status = 'enabled'
ORDER BY url
""")
return cursor.fetchall()
Импорт SEO-данных в новую CMS
def import_seo_to_strapi(strapi_api, pages_with_seo):
for page in pages_with_seo:
# Найти соответствующую страницу в новой CMS
result = strapi_api.get(f"/pages?filters[legacy_id][$eq]={page['ID']}")
if not result['data']:
continue
new_id = result['data'][0]['id']
strapi_api.put(f"/pages/{new_id}", {
'data': {
'seo': {
'metaTitle': page['seo_title'],
'metaDescription': page['seo_description'],
'canonicalURL': page['canonical'],
'metaRobots': 'noindex' if page['robots'] == '1' else 'index,follow',
'openGraph': {
'title': page['og_title'] or page['seo_title'],
'description': page['og_description'] or page['seo_description'],
}
}
}
})
Формирование таблицы 301-редиректов
import csv
def build_redirect_map(old_urls, new_urls, url_mapping_csv):
"""
Создать CSV с маппингом старых URL на новые
для настройки nginx/htaccess
"""
with open(url_mapping_csv, 'w', newline='') as f:
writer = csv.writer(f)
writer.writerow(['old_url', 'new_url', 'status_code'])
for old_url, new_url in zip(old_urls, new_urls):
if old_url != new_url:
writer.writerow([old_url, new_url, 301])
# Пример результата:
# /blog/2020/01/old-post-name → /articles/old-post-name 301
# /wp-content/uploads/img.jpg → /media/img.jpg 301
# /category/news → /blog/news 301
Nginx конфигурация редиректов
def generate_nginx_redirects(redirect_map_csv):
"""Генерировать nginx map из CSV"""
lines = ['map $request_uri $redirect_uri {', ' default "";']
with open(redirect_map_csv) as f:
reader = csv.DictReader(f)
for row in reader:
old = row['old_url'].rstrip('/')
new = row['new_url']
lines.append(f' "{old}" "{new}";')
# Также без trailing slash
if old != '/':
lines.append(f' "{old}/" "{new}";')
lines.append('}')
return '\n'.join(lines)
# nginx конфиг
nginx_config = """
include /etc/nginx/redirects.map;
server {
if ($redirect_uri != "") {
return 301 $redirect_uri;
}
}
"""
Schema.org разметка
// Пример: Article schema для блог-поста
{
"@context": "https://schema.org",
"@type": "Article",
"headline": "{{ page.seo_title }}",
"description": "{{ page.seo_description }}",
"image": "{{ page.og_image }}",
"author": {
"@type": "Person",
"name": "{{ post.author.name }}"
},
"publisher": {
"@type": "Organization",
"name": "Company Name",
"logo": { "@type": "ImageObject", "url": "https://site.com/logo.png" }
},
"datePublished": "{{ post.published_at }}",
"dateModified": "{{ post.updated_at }}"
}
Валидация SEO после миграции
def validate_seo_migration(old_sitemap_urls, new_site_base):
errors = []
for url in old_sitemap_urls:
path = url.replace('https://old-site.com', '')
response = requests.get(f"{new_site_base}{path}", allow_redirects=True)
if response.status_code == 404:
errors.append(f"404: {path}")
elif response.history:
# Проверить что редирект правильный
final = response.url
if '404' in final:
errors.append(f"Redirect to 404: {path} → {final}")
return errors
Срок выполнения
Экспорт SEO-данных, импорт в новую CMS, генерация nginx-редиректов — 2–3 рабочих дня.







