Sari la conținut
Server și .htaccess

Redirecționări htaccess în WordPress: 301 făcut corect

Mută un URL fără să pierzi pozițiile: ce directivă .htaccess folosești în WordPress, unde trebuie să stea regulile și cum le testezi fără să rămâi blocat afară.

Publicat

Muți un URL și vrei ca cel vechi să își păstreze pozițiile? Ai nevoie de un 301 în .htaccess, pus acolo unde WordPress nu îl va șterge, scris cu directiva potrivită și îndreptat exact către URL-ul pe care WordPress îl servește cu adevărat. Greșești oricare dintre aceste trei lucruri și obții un lanț de redirecționări, o buclă sau un 500 care te încuie pe dinafara wp-admin.

Întâi decizia, apoi poziția, apoi testul — în ordinea asta.

Ce directivă: Redirect, RedirectMatch sau RewriteRule

Sunt implicate două module Apache și nu sunt interschimbabile.

Redirect (mod_alias) — o cale cunoscută către o destinație. Cel mai simplu lucru care funcționează:

Redirect 301 /old-page/ https://example.com/new-page/

Două comportamente de reținut. Se potrivește pe un prefix de cale, nu pe un șir exact, deci /old-page/ prinde și /old-page/anything/ și adaugă /anything/ la destinație. Și transportă automat șirul de interogare.

RedirectMatch (mod_alias) — o expresie regulată care acoperă multe URL-uri. Așa muți o secțiune întreagă:

RedirectMatch 301 ^/blog/(.*)$ https://example.com/articles/$1

Grupul de captură (.*) devine $1 în destinație, deci /blog/hello-world/ ajunge pe /articles/hello-world/. Atenție la bara de la început: tiparele mod_alias se potrivesc pe calea completă a URL-ului.

RewriteRule (mod_rewrite) — necesară atunci când redirecționarea depinde de o condiție: numele gazdei, protocolul, un șir de interogare, un agent de utilizator. Nimic din mod_alias nu poate testa așa ceva.

RewriteEngine On
RewriteCond %{HTTP_HOST} ^example\.com$ [NC]
RewriteRule ^(.*)$ https://www.example.com/$1 [R=301,L]

Într-un .htaccess la nivel de director, tiparului i se taie bara de la început — de aceea aici scrie ^(.*)$, iar în exemplul mod_alias ^/blog/. Confuzia dintre cele două este de departe cel mai frecvent motiv pentru care o regulă copiată nu face absolut nimic, în tăcere.

Alege implicit mod_alias. Recurge la RewriteRule doar când chiar ai nevoie de o condiție. Mai puține expresii regulate, mai puține moduri de a greși.

Unde trebuie să stea regulile

Redirecționările proprii se pun deasupra liniei # BEGIN WordPress. Nu înăuntrul ei.

# --- custom redirects ---
Redirect 301 /old-page/ https://example.com/new-page/
RedirectMatch 301 ^/blog/(.*)$ https://example.com/articles/$1

# BEGIN WordPress
# The directives (lines) between "BEGIN WordPress" and "END WordPress" are
# dynamically generated, and should only be modified via WordPress filters.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress

Există două motive independente:

  1. WordPress își rescrie propriul bloc. Salvarea în Setări → Legături permanente apelează flush_rewrite_rules(), care aruncă tot ce se află între marcaje și îl generează din nou. O regulă pe care ai pus-o înăuntru dispare data viitoare când cineva atinge acel ecran — poate luni mai târziu, fără ca cineva să lege cele două evenimente.
  2. Ordinea decide cine câștigă. Blocul WordPress se termină cu o regulă generală care trimite fiecare cerere care nu e fișier și nici director către index.php. O RewriteRule pusă după ea nu rulează niciodată.

O complicație pe care merită să o spunem cinstit: mod_alias și mod_rewrite nu rulează de fapt în ordinea din fișier. Apache procesează mod_alias în timpul traducerii URL-ului, iar mod_rewrite la nivel de director mai târziu, în faza de fixup — așa că o linie Redirect poate învinge o RewriteRule care apare deasupra ei în fișier. Dacă ajungi să ai nevoie de amândouă pe căi care se suprapun, alege un singur modul pentru acea cale și rămâi la el. Depanarea unei dispute între mod_alias și mod_rewrite nu merită ora aceea.

Bara oblică de la final

Redirecționarea canonică proprie a WordPress adaugă o bară oblică la finalul majorității legăturilor permanente. Deci asta:

Redirect 301 /old-page/ https://example.com/new-page

produce două salturi — 301-ul tău către URL-ul fără bară, apoi 301-ul propriu al WordPress care adaugă bara. Lanțurile transmit în continuare semnalele de clasare, dar irosesc bugetul de accesare și adaugă un drum dus-întors pentru fiecare vizitator.

Încarcă întâi destinația într-un browser, copiază URL-ul exact așa cum se așază în bara de adrese și folosește-l pe acela. Dacă structura ta de legături permanente se termină în .html sau nu are bară finală, potrivește-te după ea. Nu există un răspuns universal corect aici, ci doar „potrivește ce servește WordPress”.

Testarea fără să te încui pe dinafară

.htaccess este citit la fiecare cerere. O eroare de sintaxă întoarce un 500 pentru tot site-ul, wp-admin inclusiv, deci calea de întoarcere trebuie să existe înainte să ai nevoie de ea.

Fă întâi o copie a fișierului. Prin SSH:

cp /path/to/wordpress/.htaccess /path/to/wordpress/.htaccess.bak

Dacă site-ul dă 500, redenumești fișierul stricat înapoi peste el și site-ul revine imediat. Ține deschisă o sesiune SFTP într-o altă fereastră — autentificarea de la zero cu site-ul căzut este momentul în care începe panica. Reține și că apachectl configtest nu analizează .htaccess, deci va raporta o configurație sănătoasă în timp ce site-ul tău este mort.

Testează cu curl, nu cu un browser. Browserele memorează dur răspunsurile 301 și îți vor arăta bucuroase rezultatul de ieri:

curl -sI https://example.com/old-page/ | head -n 5

Citește două lucruri: linia de stare trebuie să spună 301, iar antetul Location: trebuie să fie exact URL-ul final. Ca să vezi tot lanțul, urmărește-l:

curl -sIL https://example.com/old-page/ | grep -E '^(HTTP|Location)'

Mai mult de o linie HTTP/1.1 301 înseamnă că ai construit un lanț. Mai mult de aproximativ cinci înseamnă că ai construit o buclă, iar curl se va opri și îți va spune asta.

Folosește 302 cât timp încă nu ești sigur. Un 302 nu este memorat în același fel, deci o greșeală se poate anula în câteva secunde. Treci pe 301 după ce curl arată destinația pe care ai vrut-o. Nu costă nimic și a salvat mai multe site-uri decât orice alt obicei din acest articol.

Dacă o regulă pare că nu face absolut nimic, confirmă întâi că Apache citește măcar fișierul. AllowOverride None pe director face Apache să ignore complet .htaccess, iar nginx îl ignoră cu totul — verifică antetul Server: cu curl -I înainte să depanezi expresii regulate. Ca să compui blocul cu sintaxa potrivită pentru versiunea ta de Apache și pentru calea de instalare, folosește generatorul .htaccess pentru WordPress.

Când să nu folosești deloc .htaccess

Pentru câteva redirecționări pe care editorii trebuie să le administreze singuri, un plugin de redirecționări care ține regulile în baza de date este unealta mai bună: supraviețuiește mutării la alt găzduitor și nu cere SSH. Compromisul este real — php trebuie să pornească pentru a servi redirecționarea, iar asta e mai lent decât dacă răspunde direct Apache.

Folosește .htaccess pentru mutări structurale și permanente: schimbarea domeniului, redenumirea unei secțiuni, forțarea https sau www. Folosește un plugin pentru redirecționări editoriale izolate. Amândouă deodată sunt în regulă, atâta timp cât știi ce strat deține ce URL — pentru că o redirecționare definită în două locuri este o eroare care așteaptă o după-amiază proastă.

FAQ

Întrebări

Unde se pun redirecționările proprii în fișierul .htaccess al WordPress?

Deasupra liniei # BEGIN WordPress, niciodată între marcaje. WordPress regenerează tot ce se află între acele marcaje ori de câte ori cineva salvează ecranul Legături permanente, deci o regulă pusă înăuntru dispare fără avertisment. Poziția contează și pentru ordine: blocul WordPress se încheie cu o regulă generală care trimite toate cererile nepotrivite către index.php.

Să folosesc Redirect, RedirectMatch sau RewriteRule pentru un 301?

Folosește Redirect pentru o singură cale cunoscută, RedirectMatch când un singur tipar acoperă multe URL-uri și RewriteRule când redirecționarea depinde de o condiție precum numele gazdei, protocolul sau șirul de interogare. Redirect și RedirectMatch vin din mod_alias și sunt mai simple. RewriteRule vine din mod_rewrite și este singura care poate testa condiții.

De ce produce redirecționarea mea din .htaccess o buclă?

De obicei destinația se potrivește în continuare cu regula care l-a trimis acolo pe vizitator, așa că regula se declanșează la nesfârșit. Cealaltă cauză frecventă este forțarea https în spatele unui echilibrator de trafic sau al unui CDN, unde serverul vede http simplu la fiecare cerere, deși vizitatorul este deja pe o conexiune securizată. Testează în schimb antetul X-Forwarded-Proto.

Contează bara oblică finală într-o redirecționare WordPress?

Da. Redirecționarea canonică a WordPress adaugă o bară oblică la finalul majorității legăturilor permanente, deci un 301 care indică spre un URL fără bară produce două salturi în loc de unul. Redirecționările înlănțuite transmit în continuare semnalele de clasare, dar irosesc bugetul de accesare și încetinesc vizitatorul. Potrivește exact URL-ul final pe care îl servește WordPress, inclusiv bara.

Cum testez o redirecționare din .htaccess fără să îmi stric site-ul?

Rulează curl cu opțiunea care cere doar antetele pe URL-ul vechi și citește codul de stare și antetul Location înainte să te încrezi într-un browser. Browserele memorează agresiv răspunsurile 301 și îți vor arăta un rezultat vechi. Păstrează întâi o copie a fișierului funcțional, fiindcă o eroare de sintaxă în .htaccess întoarce un 500 pe fiecare pagină, inclusiv pe wp-admin.

De ce a întors tot site-ul o eroare 500 după ce am adăugat o redirecționare?

O singură directivă greșită în .htaccess doboară fiecare URL din directorul respectiv, inclusiv panoul de administrare. Cauzele obișnuite sunt un bloc IfModule neînchis, o linie RewriteEngine On lipsă sau sintaxa de acces din Apache 2.2 și 2.4 amestecată în același fișier. Șterge ultimul bloc adăugat și reîncarcă pentru confirmare.