Налаштування 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 з новими (таблиця маппінгу)
- Пріоритизувати сторінки з трафіком
Таблиця маппінгу:
| Старий 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); // пропустити заголовок
$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 дні залежно від обсягу маппінгу та складності структури.







