Μετάβαση στο περιεχόμενο
Server & .htaccess

Κανόνες ανακατεύθυνσης .htaccess στο WordPress: σωστά 301

Μετακίνησε ένα URL χωρίς να χάσεις κατατάξεις: ποια οδηγία ανακατεύθυνσης .htaccess να χρησιμοποιήσεις στο WordPress, πού ακριβώς πρέπει να μπουν οι κανόνες και πώς να τους δοκιμάσεις χωρίς να κλειδωθείς έξω.

Δημοσιεύτηκε

Μετακινείς ένα URL και θέλεις το παλιό να κρατήσει τις κατατάξεις του; Χρειάζεσαι ένα 301 στο .htaccess, τοποθετημένο εκεί που το WordPress δεν θα το σβήσει, γραμμένο με τη σωστή οδηγία και στραμμένο στο ακριβές URL που πραγματικά σερβίρει το WordPress. Κάνε λάθος σε οποιοδήποτε από τα τρία και παίρνεις αλυσίδα ανακατευθύνσεων, βρόχο, ή ένα 500 που σε κλειδώνει έξω από το wp-admin.

Πρώτα η επιλογή της οδηγίας, μετά ο κανόνας τοποθέτησης, μετά η δοκιμή — με αυτή τη σειρά.

Ποια οδηγία: Redirect, RedirectMatch ή RewriteRule

Στο παιχνίδι υπάρχουν δύο modules του Apache και δεν είναι εναλλάξιμα.

Redirect (mod_alias) — μία γνωστή διαδρομή προς έναν προορισμό. Το απλούστερο που δουλεύει:

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

Δύο συμπεριφορές που πρέπει να ξέρεις. Ταιριάζει με πρόθεμα διαδρομής, όχι με ακριβές αλφαριθμητικό, οπότε το /old-page/ πιάνει και το /old-page/anything/ και προσθέτει το /anything/ στον προορισμό. Και μεταφέρει αυτόματα το query string.

RedirectMatch (mod_alias) — μία κανονική έκφραση που καλύπτει πολλά URL. Έτσι μετακινείς ολόκληρη ενότητα:

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

Η ομάδα σύλληψης (.*) γίνεται $1 στον προορισμό, οπότε το /blog/hello-world/ προσγειώνεται στο /articles/hello-world/. Πρόσεξε την αρχική κάθετο: τα μοτίβα του mod_alias ταιριάζουν με την πλήρη διαδρομή του URL.

RewriteRule (mod_rewrite) — απαραίτητο όταν η ανακατεύθυνση εξαρτάται από μια συνθήκη: το hostname, το πρωτόκολλο, ένα query string, έναν user agent. Τίποτα στο mod_alias δεν μπορεί να ελέγξει αυτά.

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

Στο μοτίβο ενός .htaccess ανά κατάλογο η αρχική κάθετος αφαιρείται — γι’ αυτό εδώ είναι ^(.*)$ ενώ στο παράδειγμα του mod_alias ήταν ^/blog/. Αυτό ακριβώς το μπέρδεμα είναι η πιο συχνή αιτία που ένας κανόνας κάνει σιωπηλά το τίποτα.

Ξεκίνα με mod_alias ως προεπιλογή. Πιάσε το RewriteRule μόνο όταν χρειάζεσαι συνθήκη. Λιγότερες κανονικές εκφράσεις, λιγότεροι τρόποι να κάνεις λάθος.

Πού πρέπει να μπουν οι κανόνες

Οι δικές σου ανακατευθύνσεις ανήκουν πάνω από τη γραμμή # BEGIN WordPress. Όχι μέσα της.

# --- 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

Δύο ανεξάρτητοι λόγοι:

  1. Το WordPress ξαναγράφει το δικό του μπλοκ. Η αποθήκευση στο Ρυθμίσεις → Μόνιμοι σύνδεσμοι καλεί την flush_rewrite_rules(), που πετάει τα πάντα ανάμεσα στους δείκτες και τα ξαναδημιουργεί. Ένας κανόνας που έβαλες μέσα εξαφανίζεται την επόμενη φορά που κάποιος αγγίξει αυτή την οθόνη — ίσως μήνες αργότερα, χωρίς κανείς να συνδέσει τα δύο γεγονότα.
  2. Η σειρά αποφασίζει ποιος κερδίζει. Το μπλοκ του WordPress τελειώνει με έναν γενικό κανόνα που στέλνει κάθε αίτημα που δεν είναι αρχείο ή κατάλογος στο index.php. Ένα RewriteRule τοποθετημένο μετά από αυτόν δεν τρέχει ποτέ.

Μια ειλικρινής περιπλοκή: το mod_alias και το mod_rewrite στην πραγματικότητα δεν εκτελούνται με τη σειρά του αρχείου. Ο Apache επεξεργάζεται το mod_alias κατά τη μετάφραση του URL και το mod_rewrite ανά κατάλογο αργότερα, στη φάση fixup — οπότε μια γραμμή Redirect μπορεί να κερδίσει ένα RewriteRule που εμφανίζεται πιο πάνω στο αρχείο. Αν βρεθείς να χρειάζεσαι και τα δύο σε διαδρομές που επικαλύπτονται, διάλεξε ένα module για αυτή τη διαδρομή και μείνε μέσα του. Το να ξεμπλέκεις μια κόντρα mod_alias εναντίον mod_rewrite δεν αξίζει την ώρα που θα φας.

Τελικές κάθετοι

Η ίδια η κανονική ανακατεύθυνση του WordPress προσθέτει τελική κάθετο στους περισσότερους μόνιμους συνδέσμους. Οπότε αυτό:

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

παράγει δύο άλματα — το δικό σου 301 προς το URL χωρίς κάθετο, και μετά το δικό του 301 που προσθέτει την κάθετο. Οι αλυσίδες εξακολουθούν να περνούν σήματα κατάταξης, αλλά σπαταλούν crawl budget και προσθέτουν έναν γύρο για κάθε επισκέπτη.

Φόρτωσε πρώτα τον προορισμό σε browser, αντίγραψε το URL ακριβώς όπως καταλήγει στη γραμμή διευθύνσεων, και χρησιμοποίησε αυτό. Αν η δομή των μόνιμων συνδέσμων σου τελειώνει σε .html ή δεν έχει τελική κάθετο, ταίριαξε αυτό αντ’ αυτού. Δεν υπάρχει καθολικά σωστή απάντηση εδώ, μόνο «ταίριαξε ό,τι σερβίρει το WordPress».

Δοκιμή χωρίς να κλειδωθείς έξω

Το .htaccess διαβάζεται σε κάθε αίτημα. Ένα συντακτικό λάθος επιστρέφει 500 για ολόκληρο το site, μαζί και το wp-admin, οπότε ο δρόμος επιστροφής πρέπει να υπάρχει πριν τον χρειαστείς.

Κράτα πρώτα αντίγραφο του αρχείου. Μέσω SSH:

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

Αν το site βγάλει 500, μετονόμασε το αντίγραφο πάνω από το χαλασμένο και το site επιστρέφει αμέσως. Κράτα μια συνεδρία SFTP ήδη ανοιχτή σε άλλο παράθυρο — το να συνδέεσαι από την αρχή ενώ το site είναι πεσμένο είναι εκεί που ξεκινάει ο πανικός. Σημείωσε ότι το apachectl configtest δεν διαβάζει το .htaccess, οπότε θα αναφέρει υγιή διαμόρφωση ενώ το site σου είναι νεκρό.

Δοκίμασε με curl, όχι με browser. Οι browsers κρατούν σκληρά στην cache τις αποκρίσεις 301 και θα σου δείξουν ευχαρίστως το χθεσινό αποτέλεσμα:

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

Διάβασε δύο πράγματα: η γραμμή κατάστασης πρέπει να λέει 301, και η κεφαλίδα Location: πρέπει να είναι το ακριβές τελικό URL. Για να δεις όλη την αλυσίδα, ακολούθησέ την:

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

Πάνω από μία γραμμή HTTP/1.1 301 σημαίνει ότι έφτιαξες αλυσίδα. Πάνω από πέντε περίπου σημαίνει ότι έφτιαξες βρόχο, και το curl θα σταματήσει και θα στο πει.

Χρησιμοποίησε 302 όσο δεν είσαι σίγουρος. Ένα 302 δεν αποθηκεύεται στην cache με τον ίδιο τρόπο, οπότε ένα λάθος αναιρείται σε δευτερόλεπτα. Πέρνα σε 301 μόλις το curl δείξει τον προορισμό που ήθελες. Δεν κοστίζει τίποτα και έχει σώσει περισσότερα sites από κάθε άλλη συνήθεια αυτού του άρθρου.

Αν ένας κανόνας δείχνει να μην κάνει απολύτως τίποτα, επιβεβαίωσε ότι ο Apache διαβάζει καν το αρχείο. Το AllowOverride None στον κατάλογο κάνει τον Apache να αγνοεί εντελώς το .htaccess, και ο nginx το αγνοεί πάντα — έλεγξε την κεφαλίδα Server: με curl -I πριν αρχίσεις να αποσφαλματώνεις κανονικές εκφράσεις. Για να συνθέσεις το μπλοκ με τη σωστή σύνταξη για την έκδοση Apache και τη διαδρομή εγκατάστασής σου, χρησιμοποίησε τη γεννήτρια .htaccess για WordPress.

Πότε να μη χρησιμοποιήσεις καθόλου .htaccess

Για μια χούφτα ανακατευθύνσεις που πρέπει να διαχειρίζονται μόνοι τους οι συντάκτες, ένα πρόσθετο ανακατευθύνσεων που κρατά τους κανόνες στη βάση είναι το καλύτερο εργαλείο — επιβιώνει από μεταφορές σε άλλον host και δεν απαιτεί SSH. Το αντάλλαγμα είναι υπαρκτό: πρέπει να ξεκινήσει η PHP για να σερβιριστεί η ανακατεύθυνση, κάτι πιο αργό από το να απαντήσει απευθείας ο Apache.

Χρησιμοποίησε .htaccess για δομικές, μόνιμες μετακινήσεις — αλλαγή domain, μετονομασία ενότητας, επιβολή HTTPS ή www. Χρησιμοποίησε πρόσθετο για μεμονωμένες συντακτικές ανακατευθύνσεις. Το να κάνεις και τα δύο είναι μια χαρά, αρκεί να ξέρεις ποιο επίπεδο κατέχει ποιο URL, γιατί μια ανακατεύθυνση ορισμένη σε δύο σημεία είναι ένα bug που περιμένει το κακό του απόγευμα.

FAQ

Ερωτήσεις

Πού μπαίνουν οι δικές μου ανακατευθύνσεις στο αρχείο .htaccess του WordPress;

Πάνω από τη γραμμή # BEGIN WordPress, ποτέ ανάμεσα στους δείκτες. Το WordPress ξαναδημιουργεί τα πάντα μέσα σε αυτούς τους δείκτες κάθε φορά που κάποιος αποθηκεύει την οθόνη των μόνιμων συνδέσμων, οπότε ένας κανόνας τοποθετημένος εκεί μέσα διαγράφεται χωρίς προειδοποίηση. Η θέση μετράει και για τη σειρά: το μπλοκ του WordPress τελειώνει με έναν γενικό κανόνα που στέλνει όσα αιτήματα δεν ταίριαξαν στο index.php.

Να χρησιμοποιήσω Redirect, RedirectMatch ή RewriteRule για ένα 301;

Χρησιμοποίησε Redirect για μία γνωστή διαδρομή, RedirectMatch όταν ένα μοτίβο καλύπτει πολλά URL, και RewriteRule όταν η ανακατεύθυνση εξαρτάται από μια συνθήκη όπως το hostname, το πρωτόκολλο ή το query string. Τα Redirect και RedirectMatch προέρχονται από το mod_alias και είναι απλούστερα. Το RewriteRule προέρχεται από το mod_rewrite και είναι το μόνο που μπορεί να ελέγξει συνθήκες.

Γιατί η ανακατεύθυνσή μου στο .htaccess προκαλεί βρόχο;

Συνήθως ο προορισμός εξακολουθεί να ταιριάζει με τον ίδιο κανόνα που έστειλε εκεί τον επισκέπτη, οπότε ο κανόνας ενεργοποιείται ξανά και ξανά. Η άλλη συχνή αιτία είναι η επιβολή HTTPS πίσω από load balancer ή CDN, όπου ο server βλέπει απλό HTTP σε κάθε αίτημα παρότι ο επισκέπτης είναι ήδη σε HTTPS. Έλεγξε την κεφαλίδα X-Forwarded-Proto αντ' αυτού.

Παίζει ρόλο η κάθετος στο τέλος σε μια ανακατεύθυνση WordPress;

Ναι. Η κανονική ανακατεύθυνση του WordPress προσθέτει τελική κάθετο στους περισσότερους μόνιμους συνδέσμους, οπότε ένα 301 προς URL χωρίς κάθετο παράγει δύο άλματα αντί για ένα. Οι αλυσιδωτές ανακατευθύνσεις εξακολουθούν να περνούν σήματα κατάταξης, αλλά σπαταλούν crawl budget και καθυστερούν τον επισκέπτη. Ταίριαξε το ακριβές τελικό URL που σερβίρει το WordPress, μαζί με την κάθετο.

Πώς δοκιμάζω μια ανακατεύθυνση .htaccess χωρίς να χαλάσω το site μου;

Τρέξε curl με τη σημαία μόνο για κεφαλίδες στο παλιό URL και διάβασε τον κωδικό κατάστασης και την κεφαλίδα Location πριν εμπιστευτείς τον browser. Οι browsers αποθηκεύουν επιθετικά στην cache τις αποκρίσεις 301 και θα σου δείξουν παλιό αποτέλεσμα. Κράτα πρώτα αντίγραφο του αρχείου που δουλεύει, γιατί ένα συντακτικό λάθος στο .htaccess επιστρέφει 500 σε κάθε σελίδα, μαζί και στο wp-admin.

Γιατί όλο μου το site επέστρεψε σφάλμα 500 μετά την προσθήκη μιας ανακατεύθυνσης;

Μία μόνο λανθασμένη οδηγία στο .htaccess ρίχνει κάθε URL κάτω από αυτόν τον κατάλογο, μαζί και τη διαχείριση. Συνηθισμένες αιτίες είναι ένα ανοιχτό IfModule που δεν έκλεισε, μια γραμμή RewriteEngine On που λείπει, ή ανακατεμένη σύνταξη πρόσβασης Apache 2.2 και 2.4 στο ίδιο αρχείο. Αφαίρεσε το τελευταίο μπλοκ που πρόσθεσες και ξαναφόρτωσε για επιβεβαίωση.