Настройка 301-редиректов при миграции сайта
При переезде сайта на новую структуру URL, смене домена или редизайне — 301-редиректы сохраняют накопленный SEO-вес страниц. Без них поисковики воспринимают старые URL как удалённые страницы, и позиции падают.
Что такое 301 и почему важно
301 (Moved Permanently) передаёт от 90 до 99% PageRank новой странице. Google обычно переносит позиции за 2–8 недель после корректной настройки редиректов. 302 (Moved Temporarily) — не передаёт вес, использовать только для временных переездов.
Стратегия миграции
До переезда:
- Краулинг старого сайта (Screaming Frog, Sitebulb) — выгрузить все индексируемые URL
- Проверить через Google Search Console — найти страницы с трафиком и позициями
- Сопоставить старые URL с новыми (mapping таблица)
- Приоритизировать страницы с трафиком
Таблица маппинга:
| Старый URL | Новый URL | Приоритет |
|---|---|---|
/catalog.php?cat=12 |
/catalog/smartphones |
Высокий |
/product.php?id=4521 |
/products/iphone-15-pro |
Высокий |
/about.html |
/o-kompanii |
Средний |
/news/2018/post-1 |
/blog/post-1-slug |
Низкий |
Реализация в Laravel (DB-хранилище)
Schema::create('redirects', function (Blueprint $table) {
$table->id();
$table->string('from_url', 2048)->unique();
$table->string('to_url', 2048);
$table->smallInteger('status_code')->default(301);
$table->boolean('is_regex')->default(false);
$table->integer('sort_order')->default(0);
$table->timestamps();
});
// app/Http/Middleware/HandleRedirects.php
class HandleRedirects
{
public function handle(Request $request, Closure $next): Response
{
$path = '/' . ltrim($request->getPathInfo(), '/');
// Точное совпадение из Redis-кеша
$redirect = Cache::remember("redirect:{$path}", 3600, function () use ($path) {
return Redirect::where('from_url', $path)
->where('is_regex', false)
->first();
});
if ($redirect) {
return redirect($redirect->to_url, $redirect->status_code);
}
// Regex-редиректы (менее производительны, без кеша)
$regexRedirects = Cache::remember('redirects:regex', 3600, function () {
return Redirect::where('is_regex', true)
->orderBy('sort_order')
->get();
});
foreach ($regexRedirects as $redirect) {
if (preg_match($redirect->from_url, $path, $matches)) {
$to = preg_replace($redirect->from_url, $redirect->to_url, $path);
return redirect($to, $redirect->status_code);
}
}
return $next($request);
}
}
Массовый импорт из CSV
// Artisan command: import:redirects storage/redirects.csv
public function handle(): void
{
$file = fopen($this->argument('file'), 'r');
fgetcsv($file); // skip header
$batch = [];
while ($row = fgetcsv($file)) {
$batch[] = [
'from_url' => trim($row[0]),
'to_url' => trim($row[1]),
'status_code' => (int) ($row[2] ?? 301),
'created_at' => now(),
'updated_at' => now(),
];
if (count($batch) >= 500) {
Redirect::upsert($batch, ['from_url'], ['to_url', 'status_code']);
$batch = [];
}
}
if ($batch) {
Redirect::upsert($batch, ['from_url'], ['to_url', 'status_code']);
}
Cache::flush(); // сброс кеша редиректов
$this->info('Импорт завершён');
}
Nginx-конфигурация для массовых редиректов
При тысячах редиректов — вынести в Nginx map (производительнее middleware):
map $uri $redirect_to {
include /etc/nginx/redirects.map;
}
server {
if ($redirect_to) {
return 301 $redirect_to;
}
}
# redirects.map
/old-page /new-page;
/catalog.php /catalog;
Генерация redirects.map скриптом из базы данных при деплое.
Редирект домена
server {
listen 80;
server_name old-domain.ru www.old-domain.ru;
return 301 https://new-domain.ru$request_uri;
}
Проверка после миграции
- Screaming Frog: краулинг нового сайта, проверка цепочек редиректов (не допускать цепочки длиннее 1 шага)
- Google Search Console: мониторинг Coverage → Not Found (404)
- Ahrefs/Semrush: проверка что бэклинки корректно redirected
- Проверить
www.иnon-www.версию домена
Срок настройки: 1–3 дня в зависимости от объёма маппинга и сложности структуры.







