خطای ERR_TOO_MANY_REDIRECTS را دیدهاید؟
مرورگر را باز میکنید، آدرس سایت را میزنید و به جای صفحه اصلی، با خطای ERR_TOO_MANY_REDIRECTS مواجه میشوید. یا در وردپرس، پیام «حلقه بازنشانی بیش از حد» را میبینید. مشکل از جایی شروع شد که تصمیم گرفتید سایت را به HTTPS منتقل کنید و یک قانون ریدایرکت در .htaccess گذاشتید. حالا سایت از کار افتاده و کاربران پیام خطا میبینند.
این سناریو را بارها دیدهام. راهحل، حذف قانون اضافی نیست؛ درک این است که کجا ریدایرکت را اجرا کنید و کجا نه. در این مقاله، نصب SSL روی هاست لینوکس، تمدید خودکار گواهی و قاعدهای که HTTPS را بدون ساخت حلقه اجبار میکند، موشکافی میشود.
گواهی SSL را از کجا بگیریم؟ صدور خودکار با Let's Encrypt
برای نصب SSL روی هاست، دو مسیر اصلی دارید: خرید گواهی از مرجع تجاری یا استفاده از Let's Encrypt که رایگان است و هر ۹۰ روز یکبار تمدید میشود. در هاستهای لینوکس با کنترلپنل cPanel یا DirectAdmin، معمولاً ابزار AutoSSL از قبل فعال است و گواهی را خودش صادر و نصب میکند.
اگر هاست شما AutoSSL ندارد، از سرویسدهنده بخواهید فعالش کند. این ابزار از ACME protocol استفاده میکند و گواهی را برای دامنه اصلی و زیردامنهها صادر میکند. در هاست لینوکس سرورنت، AutoSSL به صورت پیشفرض روشن است و نیازی به دخالت دستی ندارید.
صدور دستی گواهی با certbot
در سرور اختصاصی یا VPS که کنترلپنل ندارد، certbot ابزار استاندارد است. نصب و صدور گواهی برای دامنه example.com با وبسرور Apache به این شکل انجام میشود:
sudo apt update
sudo apt install certbot python3-certbot-apache
sudo certbot --apache -d example.com -d www.example.com
certbot فایلهای تنظیمات Apache را پیدا میکند، گواهی را صادر و بهصورت خودکار در تنظیمات اعمال میکند. برای Nginx، بهجای python3-certbot-apache از python3-certbot-nginx استفاده کنید.
تمدید خودکار گواهی؛ جایی که سایتها از دسترس خارج میشوند
گواهی Let's Encrypt فقط ۹۰ روز اعتبار دارد. اگر تمدید نشود، مرورگرها اخطار امنیتی نشان میدهند و کاربران را از ورود به سایت منصرف میکنند. اینجا اشتباه میکنند: فرض میکنند cron job تمدید از قبل تنظیم شده است. در حالی که در بسیاری از سرورها، این کار دستی است.
برای اطمینان از تمدید خودکار، ابتدا تست کنید که تمدید واقعاً کار میکند:
sudo certbot renew --dry-run
این دستور شبیهسازی تمدید را انجام میدهد و اگر خطایی باشد، همانجا نمایش میدهد. سپس cron job را بررسی کنید:
sudo crontab -l | grep certbot
اگر خروجی نداشت، cron job را اضافه کنید. دستور زیر هر روز ساعت ۳ صبح اجرا میشود و اگر گواهی کمتر از ۳۰ روز تا انقضا داشته باشد، آن را تمدید میکند:
0 3 * * * /usr/bin/certbot renew --quiet
بعد از تمدید، سرویس وبسرور باید reload شود. certbot معمولاً این کار را خودش انجام میدهد، اما اگر از --deploy-hook استفاده نکردهاید و سرویس reload نمیشود، این خط را به cron اضافه کنید:
0 3 * * * /usr/bin/certbot renew --quiet --deploy-hook "systemctl reload apache2"
ریدایرکت HTTPS بدون حلقه؛ قانونی که باید بنویسید
حالا که گواهی نصب شده، باید همه ترافیک HTTP را به HTTPS هدایت کنید. قانون ریدایرکت در .htaccess باید دقیق باشد. قانون اشتباه، حلقه ریدایرکت میسازد. قانون درست این است:
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
شرط %{HTTPS} off یعنی فقط وقتی درخواست از HTTP آمده، ریدایرکت اجرا شود. اگر این شرط نباشد، هر درخواستی، حتی HTTPS، دوباره به HTTPS ریدایرکت میشود و حلقه ساخته میشود. این دقیقاً همان خطایی است که در ابتدای مقاله گفتیم.
ریدایرکت در سطح وبسرور؛ جایگزین مطمئنتر
در هاست اشتراکی که فقط به .htaccess دسترسی دارید، همان قانون بالا کافی است. اما در سرور اختصاصی، ریدایرکت را در Virtual Host انجام دهید. برای Apache:
<VirtualHost *:80>
ServerName example.com
Redirect permanent / https://example.com/
</VirtualHost>
این روش سریعتر است چون Apache قانون را قبل از پردازش .htaccess اجرا میکند. در Nginx هم به همین شکل در بلوک server پورت ۸۰:
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
وردپرس و SSL؛ تنظیمات WP-CONFIG و حلقه ریدایرکت
اگر سایت وردپرسی دارید، یک لایه دیگر هم هست که حلقه ریدایرکت میسازد. وردپرس آدرس سایت را در جدول wp_options ذخیره میکند. اگر این آدرس هنوز http:// باشد، وردپرس خودش یک ریدایرکت به HTTP اجرا میکند و اگر قانون .htaccess شما به HTTPS ریدایرکت کند، دو ریدایرکت متضاد ساخته میشود و حلقه شکل میگیرد.
راهحل، تعیین آدرس سایت در wp-config.php است. این دو خط را قبل از خط /* That's all, stop editing! */ اضافه کنید:
define('WP_HOME', 'https://example.com');
define('WP_SITEURL', 'https://example.com');
بعد از این تغییر، از پیشخوان وردپرس خارج و دوباره وارد شوید. اگر صفحه سفید دیدید، راهنمای رفع صفحه سفید در وردپرس را بخوانید.
محتواهای Mixed Content؛ چیزی که ریدایرکت حل نمیکند
ریدایرکت HTTPS فقط درخواستهای مرورگر را تبدیل میکند. اما اگر در صفحه، اسکریپت یا تصویری با آدرس http:// لود شود، مرورگر آن را مسدود میکند. این خطا در کنسول مرورگر به شکل Mixed Content: The page at 'https://example.com' was loaded over HTTPS, but requested an insecure resource نمایش داده میشود.
برای پیدا کردن این منابع، از ابزارهای آنلاین اسکن محتوای ترکیبی استفاده کنید یا در گوگل کروم، F12 را بزنید و تب Console را باز کنید. سپس در دیتابیس وردپرس، کوئری زیر را اجرا کنید تا همه آدرسهای HTTP پیدا شوند:
SELECT * FROM wp_posts WHERE post_content LIKE '%http://example.com%';
جایگزینی سراسری http://example.com با https://example.com در جدول wp_posts معمولاً مشکل را حل میکند. قبل از اجرا، حتماً از دیتابیس بکاپ بگیرید.
HSTS؛ لایه بعدی امنیت با یک هزینه مشخص
بعد از اطمینان از ریدایرکت درست، میتوانید هدر HSTS را فعال کنید تا مرورگر کاربر را مستقیماً به HTTPS ببرد و حتی یک درخواست HTTP هم ارسال نشود. در .htaccess:
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
هزینه این کار را صادقانه بگویم: اگر گواهی SSL شما منقضی شود و تمدید نشود، کاربران به مدت یک سال نمیتوانند به سایت شما دسترسی پیدا کنند، حتی با زدن آدرس HTTP. مرورگر بهخاطر HSTS، درخواست را قبل از ارسال مسدود میکند. اینجا اشتباه میکنند: HSTS را بدون اطمینان از تمدید خودکار فعال میکنند و بعد از انقضای گواهی، سایت برای همه کاربرانی که قبلاً مراجعه کردهاند کاملاً از دسترس خارج میشود.
پیشنهاد من: اول cron job تمدید را مطمئن شوید، یک ماه صبر کنید و بعد HSTS را با max-age=86400 (یک روز) فعال کنید. اگر همهچیز درست بود، مقدار را به یک سال افزایش دهید.
بررسی نهایی و خطایابی
بعد از اعمال تغییرات، با این دستور بررسی کنید که ریدایرکت درست کار میکند:
curl -I http://example.com
خروجی باید شامل 301 Moved Permanently و هدر Location: https://example.com/ باشد. اگر کد 302 دیدید، یعنی ریدایرکت موقت است و برای سئو مناسب نیست. اگر کد 200 روی HTTP دیدید، یعنی قانون ریدایرکت اعمال نشده است.
برای بررسی DNS و اطمینان از اینکه رکوردهای A و AAAA درست هستند، از ابزار بررسی DNS و شبکه استفاده کنید. اگر سایت را به دامنه جدیدی منتقل کردهاید، راهنمای تغییر دامنه سایت بدون افت سئو را هم بخوانید.
پرسشهای پرتکرار
چرا بعد از نصب SSL سایت من با خطای ERR_TOO_MANY_REDIRECTS مواجه میشود؟
این خطا یعنی یک حلقه ریدایرکت ساخته شده است. معمولاً به این دلیل است که قانون .htaccess شما شرط %{HTTPS} off را ندارد و همه درخواستها را دوباره به HTTPS ریدایرکت میکند. قانون صحیح را از بخش «ریدایرکت HTTPS بدون حلقه» کپی کنید و جایگزین کنید.
گواهی SSL رایگان Let's Encrypt چقدر اعتبار دارد و چگونه تمدید میشود؟
گواهی Let's Encrypt ۹۰ روز اعتبار دارد. تمدید بهصورت خودکار با cron job انجام میشود که دستور certbot renew را اجرا میکند. برای اطمینان از عملکرد آن، دستور sudo certbot renew --dry-run را اجرا کنید.
آیا بعد از نصب SSL باید آدرس سایت را در وردپرس تغییر دهم؟
بله. اگر آدرس سایت در تنظیمات وردپرس هنوز http:// باشد، وردپرس ریدایرکت به HTTP میسازد که با قانون ریدایرکت HTTPS تداخل پیدا میکند. آدرس را در تنظیمات عمومی یا فایل wp-config.php با ثابتهای WP_HOME و WP_SITEURL به HTTPS تغییر دهید.
چگونه بفهمم همه محتوای سایت من از HTTPS سرو میشود؟
کنسول مرورگر را باز کنید و تب Network را بررسی کنید. هر درخواستی که با http:// شروع شود، Mixed Content است و باید اصلاح شود. در وردپرس، با کوئری SQL روی جدول wp_posts میتوانید همه آدرسهای HTTP را پیدا و جایگزین کنید.