پرش به محتوا
سرور و .htaccess

قوانین ریدایرکت htaccess در wordpress: نوشتن صحیح 301

یک نشانی را جابه‌جا کنید بدون از دست دادن رتبه: کدام دستور ریدایرکت htaccess را در wordpress به کار ببرید، قوانین کجا باید قرار بگیرند و چطور بدون قفل شدن بیرون از سایت تستشان کنید.

منتشرشده

نشانی‌ای را جابه‌جا می‌کنید و می‌خواهید نشانی قدیمی رتبه‌اش را نگه دارد؟ به یک 301 در .htaccess نیاز دارید که جایی گذاشته شود که وردپرس پاکش نکند، با دستور درست نوشته شود و به همان نشانی‌ای اشاره کند که وردپرس واقعاً ارائه می‌دهد. هر کدام از این سه را اشتباه کنید، به یک زنجیرهٔ ریدایرکت، یک حلقه، یا خطای 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. وردپرس بلوک خودش را بازنویسی می‌کند. ذخیرهٔ تنظیمات ← پیوندهای یکتا تابع flush_rewrite_rules() را صدا می‌زند که هر چه بین نشانگرهاست دور می‌ریزد و از نو می‌سازد. قانونی که داخل گذاشته‌اید دفعهٔ بعد که کسی به آن صفحه دست بزند ناپدید می‌شود — شاید ماه‌ها بعد، بی‌آنکه کسی این دو رویداد را به هم ربط دهد.
  2. ترتیب تعیین می‌کند چه کسی برنده است. بلوک وردپرس با یک قانون فراگیر تمام می‌شود که هر درخواستی را که فایل و پوشه نیست به index.php می‌فرستد. یک RewriteRule که بعد از آن گذاشته شود هرگز اجرا نمی‌شود.

یک پیچیدگی که باید صادقانه گفت: mod_alias و mod_rewrite در واقع به ترتیب فایل اجرا نمی‌شوند. Apache ماژول mod_alias را حین ترجمهٔ نشانی پردازش می‌کند و mod_rewrite سطح‌پوشه را دیرتر، در مرحلهٔ fixup — پس یک خط Redirect می‌تواند بر یک RewriteRule که بالاتر از آن در فایل آمده غلبه کند. اگر دیدید هر دو را روی مسیرهای هم‌پوشان لازم دارید، برای آن مسیر یک ماژول انتخاب کنید و درون همان بمانید. اشکال‌زدایی نزاع mod_alias با mod_rewrite ارزش یک ساعت وقت را ندارد.

اسلش پایانی

ریدایرکت متعارف خود وردپرس به بیشتر پیوندهای یکتا یک اسلش پایانی اضافه می‌کند. پس این:

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

دو پرش می‌سازد — 301 شما به نشانی بدون اسلش، بعد 301 خود وردپرس که اسلش را اضافه می‌کند. زنجیره‌ها هنوز سیگنال‌های رتبه را منتقل می‌کنند، اما بودجهٔ خزش را هدر می‌دهند و برای هر بازدیدکننده یک رفت‌وبرگشت اضافه می‌کنند.

اول مقصد را در مرورگر باز کنید، نشانی را دقیقاً همان‌طور که در نوار آدرس ثابت می‌شود کپی کنید و همان را به کار ببرید. اگر ساختار پیوند یکتای شما به .html ختم می‌شود یا اسلش پایانی ندارد، با همان تطبیق دهید. اینجا پاسخ درستِ جهانی وجود ندارد، فقط «با آنچه وردپرس ارائه می‌دهد تطبیق بده».

تست کردن بدون قفل شدن بیرون

.htaccess در هر درخواست خوانده می‌شود. یک خطای نگارشی برای کل سایت از جمله wp-admin خطای 500 برمی‌گرداند، پس مسیر بازیابی باید پیش از آنکه لازمش داشته باشید موجود باشد.

اول از فایل پشتیبان بگیرید. با 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 به همان شکل کش نمی‌شود، پس اشتباه در چند ثانیه برگشت‌پذیر است. وقتی curl مقصدی را که در نظر داشتید نشان داد به 301 سوئیچ کنید. این کار هیچ هزینه‌ای ندارد و بیش از هر عادت دیگری در این مقاله سایت نجات داده است.

اگر قانونی انگار اصلاً هیچ کاری نمی‌کند، مطمئن شوید Apache اصلاً فایل را می‌خواند. AllowOverride None روی آن پوشه باعث می‌شود Apache به‌کلی .htaccess را نادیده بگیرد، و nginx هم کاملاً نادیده‌اش می‌گیرد — پیش از اشکال‌زدایی عبارت‌های باقاعده، سرایند Server: را با curl -I بررسی کنید. برای سرهم کردن بلوک با نگارش درست نسخهٔ Apache و مسیر نصب خودتان، از سازندهٔ htaccess وردپرس استفاده کنید.

کی اصلاً سراغ htaccess نروید

برای چند ریدایرکت انگشت‌شمار که ویراستاران باید خودشان مدیریتشان کنند، افزونه‌ای که قوانین را در پایگاه‌داده ذخیره می‌کند ابزار بهتری است — از مهاجرت میزبان جان سالم به در می‌برد و به SSH نیاز ندارد. مصالحه واقعی است: برای ارائهٔ ریدایرکت باید php بالا بیاید، که از پاسخ مستقیم Apache کندتر است.

.htaccess را برای جابه‌جایی‌های ساختاری و دائمی به کار ببرید — تغییر دامنه، تغییر نام یک بخش، اجبار https یا www. برای ریدایرکت‌های تحریریهٔ موردی از افزونه استفاده کنید. انجام هر دو اشکالی ندارد به شرطی که بدانید کدام لایه مالک کدام نشانی است، چون ریدایرکتی که در دو جا تعریف شده باشد یک باگ است که منتظر یک بعدازظهر بد نشسته.

FAQ

پرسش‌ها

ریدایرکت‌های سفارشی در فایل htaccess وردپرس کجا نوشته می‌شوند؟

بالای خط # BEGIN WordPress، هرگز بین دو نشانگر. وردپرس هر بار که کسی صفحهٔ پیوندهای یکتا را ذخیره می‌کند، همهٔ محتوای بین آن دو نشانگر را دوباره می‌سازد، پس قانونی که داخل آن گذاشته شود بدون هیچ هشداری حذف می‌شود. جای قرارگیری از نظر ترتیب هم مهم است: بلوک وردپرس با یک قانون فراگیر تمام می‌شود که هر درخواست بی‌تطابق را به index.php می‌فرستد.

برای یک 301 از Redirect استفاده کنم یا RedirectMatch یا RewriteRule؟

برای یک مسیر مشخص از Redirect، وقتی یک الگو چندین نشانی را پوشش می‌دهد از RedirectMatch، و وقتی ریدایرکت به شرطی مثل نام میزبان، پروتکل یا رشتهٔ پرس‌وجو وابسته است از RewriteRule استفاده کنید. Redirect و RedirectMatch از mod_alias می‌آیند و ساده‌ترند. RewriteRule از mod_rewrite می‌آید و تنها موردی است که می‌تواند شرط بسنجد.

چرا ریدایرکت htaccess من حلقهٔ بی‌پایان می‌سازد؟

معمولاً چون مقصد هنوز با همان قانونی که بازدیدکننده را به آنجا فرستاده مطابقت دارد، پس قانون تا ابد دوباره اجرا می‌شود. علت رایج دیگر اجبار https پشت یک متعادل‌کنندهٔ بار یا شبکهٔ توزیع محتواست، جایی که سرور در هر درخواست http ساده می‌بیند هرچند بازدیدکننده همین حالا روی https است. به جای آن سرایند X-Forwarded-Proto را بسنجید.

آیا اسلش پایانی در ریدایرکت وردپرس اهمیت دارد؟

بله. ریدایرکت متعارف خود وردپرس به بیشتر پیوندهای یکتا یک اسلش پایانی اضافه می‌کند، پس هدف گرفتن یک نشانی بدون اسلش با 301 به جای یک پرش، دو پرش می‌سازد. زنجیرهٔ ریدایرکت هنوز سیگنال‌های رتبه را منتقل می‌کند اما بودجهٔ خزش را هدر می‌دهد و بازدیدکننده را کند می‌کند. دقیقاً همان نشانی نهایی را که وردپرس ارائه می‌دهد، همراه با اسلش، هدف بگیرید.

چطور یک ریدایرکت htaccess را بدون خراب کردن سایتم تست کنم؟

پیش از اعتماد به مرورگر، curl را با پرچم فقط‌سرایند روی نشانی قدیمی اجرا کنید و کد وضعیت و سرایند Location را بخوانید. مرورگرها پاسخ‌های 301 را سرسختانه کش می‌کنند و نتیجهٔ کهنه نشانتان می‌دهند. اول از فایل سالم نسخه‌ای نگه دارید، چون یک خطای نگارشی در htaccess روی هر صفحه از جمله wp-admin خطای 500 برمی‌گرداند.

چرا بعد از افزودن یک ریدایرکت کل سایتم خطای 500 داد؟

یک دستور بدشکل در htaccess همهٔ نشانی‌های زیر آن پوشه، از جمله پیشخوان، را از کار می‌اندازد. علت‌های رایج عبارت‌اند از یک IfModule بسته‌نشده، نبودن خط RewriteEngine On، یا مخلوط شدن نگارش دسترسی Apache 2.2 و 2.4 در یک فایل. آخرین بلوکی را که افزوده‌اید بردارید و صفحه را دوباره بارگذاری کنید تا مطمئن شوید.