דילוג לתוכן
שרת ו-.htaccess

כללי הפניה ב-htaccess של wordpress: איך כותבים 301 נכון

העברת כתובת בלי לאבד דירוג: באיזו הנחיית הפניה של htaccess להשתמש ב-wordpress, איפה הכללים חייבים לשבת, ואיך לבדוק אותם בלי להינעל מחוץ לאתר.

פורסם

מעבירים כתובת ורוצים שהישנה תשמור על הדירוג שלה? אתם צריכים 301 בתוך .htaccess, ממוקם היכן ש-wordpress לא ימחק אותו, כתוב בהנחיה הנכונה, ומכוון אל הכתובת המדויקת ש-wordpress באמת מגיש. תטעו באחד משלושת אלה ותקבלו שרשרת הפניות, לולאה, או שגיאת 500 שנועלת אתכם מחוץ ל-wp-admin.

ההחלטה, כלל המיקום, והבדיקה — בסדר הזה.

איזו הנחיה: Redirect, RedirectMatch או RewriteRule

שני מודולים של Apache פועלים כאן והם אינם תחליף זה לזה.

Redirect (mod_alias) — נתיב ידוע אחד אל יעד אחד. הדבר הפשוט ביותר שעובד:

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

שתי התנהגויות שכדאי להכיר. ההתאמה נעשית על קידומת נתיב, לא על מחרוזת מדויקת, ולכן /old-page/ תופס גם את /old-page/anything/ ומצמיד /anything/ ליעד. בנוסף הוא מעביר את מחרוזת השאילתה אוטומטית.

RedirectMatch (mod_alias) — ביטוי רגולרי אחד שמכסה כתובות רבות. כך מעבירים מדור שלם:

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

קבוצת הלכידה (.*) הופכת ל-$1 ביעד, ולכן /blog/hello-world/ נוחת על /articles/hello-world/. שימו לב ללוכסן הפותח: תבניות mod_alias מתאימות לנתיב הכתובת המלא.

RewriteRule (mod_rewrite) — נדרש כשההפניה תלויה בתנאי: שם המארח, הפרוטוקול, מחרוזת שאילתה, סוכן משתמש. שום דבר ב-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 בשלב תרגום הכתובת ואת mod_rewrite ברמת התיקייה מאוחר יותר, בשלב התיקון — ולכן שורת Redirect יכולה לנצח RewriteRule שמופיע מעליה בקובץ. אם אתם מוצאים את עצמכם זקוקים לשניהם על נתיבים חופפים, בחרו מודול אחד לאותו נתיב והישארו בתוכו. איתור תקלות במאבק בין mod_alias ל-mod_rewrite לא שווה את השעה.

לוכסנים בסוף הכתובת

ההפניה הקנונית של wordpress עצמו מוסיפה לוכסן בסוף רוב הקישורים הקבועים. ולכן זה:

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

מייצר שתי קפיצות — ה-301 שלכם אל הכתובת ללא לוכסן, ואז ה-301 של wordpress שמוסיף את הלוכסן. שרשראות עדיין מעבירות אותות דירוג, אבל מבזבזות תקציב סריקה ומוסיפות נסיעה הלוך ושוב לכל גולש.

טענו קודם את היעד בדפדפן, העתיקו את הכתובת בדיוק כפי שהיא מתייצבת בשורת הכתובת, והשתמשו בה. אם מבנה הקישורים הקבועים שלכם מסתיים ב-.html או ללא לוכסן בסוף, התאימו לזה במקום. אין כאן תשובה נכונה אוניברסלית, רק ״התאימו למה ש-wordpress מגיש״.

בדיקה בלי לנעול את עצמכם בחוץ

.htaccess נקרא בכל בקשה. שגיאת תחביר מחזירה 500 לכל האתר כולל wp-admin, ולכן נתיב ההתאוששות חייב להיות קיים לפני שתזדקקו לו.

גבו קודם את הקובץ. דרך SSH:

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

אם האתר מחזיר 500, החזירו את הקובץ התקין על גבי השבור והאתר חוזר מיד. השאירו חיבור SFTP פתוח מראש בחלון אחר — התחברות מאפס בזמן שהאתר למטה היא הרגע שבו מתחילה הבהלה. שימו לב ש-apachectl configtest אינו מנתח את .htaccess, ולכן הוא ידווח על תצורה תקינה בזמן שהאתר שלכם מת.

בדקו עם curl, לא עם דפדפן. דפדפנים שומרים תשובות 301 במטמון בחוזקה וישמחו להראות לכם את התוצאה של אתמול:

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

קראו שני דברים: שורת המצב צריכה לומר 301, והכותר Location: צריך להיות בדיוק הכתובת הסופית. כדי לראות את השרשרת כולה, עקבו אחריה:

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

יותר משורת HTTP/1.1 301 אחת פירושה שבניתם שרשרת. יותר מחמש בערך פירושו שבניתם לולאה, ו-curl יעצור ויאמר לכם זאת.

השתמשו ב-302 כל עוד אינכם בטוחים. תשובת 302 אינה נשמרת במטמון באותו אופן, ולכן טעות הפיכה תוך שניות. עברו ל-301 כש-curl מראה את היעד שהתכוונתם אליו. זה לא עולה כלום והציל יותר אתרים מכל הרגל אחר בכתבה הזאת.

אם כלל נראה כאילו אינו עושה דבר, ודאו ש-Apache בכלל קורא את הקובץ. AllowOverride None על התיקייה גורם ל-Apache להתעלם לגמרי מ-.htaccess, ו-nginx מתעלם ממנו כליל — בדקו את הכותר Server: עם curl -I לפני שאתם מנפים ביטויים רגולריים. כדי להרכיב את הבלוק בתחביר הנכון לגרסת Apache ולנתיב ההתקנה שלכם, השתמשו במחולל htaccess של wordpress.

מתי לא להשתמש ב-htaccess בכלל

עבור קומץ הפניות שעורכי תוכן צריכים לנהל בעצמם, תוסף הפניות ששומר את הכללים במסד הנתונים הוא הכלי הטוב יותר — הוא שורד הגירת אחסון ואינו דורש SSH. הפשרה אמיתית: php חייב לעלות כדי להגיש את ההפניה, וזה איטי יותר מ-Apache שעונה ישירות.

השתמשו ב-.htaccess להעברות מבניות וקבועות — החלפת דומיין, שינוי שם של מדור, כפיית https או www. השתמשו בתוסף להפניות מערכתיות נקודתיות. שילוב של שניהם בסדר גמור כל עוד אתם יודעים איזו שכבה מחזיקה איזו כתובת, כי הפניה שמוגדרת בשני מקומות היא באג שמחכה לאחר צהריים גרוע.

FAQ

שאלות

איפה ממקמים הפניות מותאמות אישית בקובץ htaccess של wordpress?

מעל השורה # BEGIN WordPress, לעולם לא בין הסמנים. wordpress מייצר מחדש את כל מה שנמצא בין הסמנים בכל פעם שמישהו שומר את מסך הקישורים הקבועים, ולכן כלל שהונח בפנים נמחק בלי אזהרה. המיקום חשוב גם מבחינת סדר: הבלוק של wordpress מסתיים בכלל גורף ששולח כל בקשה שלא נתפסה אל index.php.

להשתמש ב-Redirect, ב-RedirectMatch או ב-RewriteRule עבור 301?

השתמשו ב-Redirect לנתיב בודד ידוע, ב-RedirectMatch כשתבנית אחת מכסה כתובות רבות, וב-RewriteRule כשההפניה תלויה בתנאי כמו שם המארח, הפרוטוקול או מחרוזת השאילתה. Redirect ו-RedirectMatch מגיעים מ-mod_alias והם פשוטים יותר. RewriteRule מגיע מ-mod_rewrite והוא היחיד שיכול לבדוק תנאים.

למה ההפניה שלי ב-htaccess יוצרת לולאה?

בדרך כלל היעד עדיין תואם לאותו כלל ששלח אליו את הגולש, ולכן הכלל נורה שוב ושוב לנצח. הסיבה הנפוצה השנייה היא כפיית https מאחורי מאזן עומסים או רשת הפצת תוכן, שם השרת רואה http רגיל בכל בקשה למרות שהגולש כבר על https. בדקו במקום זאת את הכותר X-Forwarded-Proto.

האם לוכסן בסוף הכתובת משנה בהפניה של wordpress?

כן. ההפניה הקנונית של wordpress מוסיפה לוכסן בסוף רוב הקישורים הקבועים, ולכן הפניית 301 אל כתובת בלי לוכסן מייצרת שתי קפיצות במקום אחת. שרשרת הפניות עדיין מעבירה אותות דירוג אבל מבזבזת תקציב סריקה ומאטה את הגולש. התאימו בדיוק לכתובת הסופית ש-wordpress מגיש, כולל הלוכסן.

איך בודקים הפניה ב-htaccess בלי לשבור את האתר?

הריצו curl עם דגל הכותרים בלבד מול הכתובת הישנה וקראו את קוד המצב ואת הכותר Location לפני שאתם סומכים על הדפדפן. דפדפנים שומרים תשובות 301 במטמון בעקשנות ויציגו לכם תוצאה מיושנת. שמרו קודם עותק של הקובץ התקין, כי שגיאת תחביר אחת ב-htaccess מחזירה 500 בכל עמוד, כולל wp-admin.

למה כל האתר שלי החזיר שגיאת 500 אחרי שהוספתי הפניה?

הנחיה פגומה אחת ב-htaccess מפילה כל כתובת מתחת לאותה תיקייה, כולל ממשק הניהול. סיבות נפוצות הן עטיפת IfModule שלא נסגרה, שורת RewriteEngine On חסרה, או ערבוב תחביר ההרשאות של Apache 2.2 ו-2.4 באותו קובץ. הסירו את הבלוק האחרון שהוספתם ורעננו כדי לוודא.