SSL و امنیت

رفع خطای mixed content وردپرس بعد از نصب SSL

خطای mixed content وردپرس یعنی صفحه با HTTPS باز شده، ولی بخشی از آن مثل تصویر، CSS یا اسکریپت هنوز با آدرس http صدا زده می شود و مرورگر قفل امن را برمی دارد. مسیر رفع آن معمولا این است: آدرس های http را در کنسول مرورگر پیدا کنید، در دیتابیس به https تغییر بدهید و کش را پاک کنید.

نوشته تیم فنی پیکوهاستبه روزرسانی: ۱۸ مهر ۱۴۰۵۸ دقیقه مطالعه

اگر بعد از نصب گواهی SSL قفل مرورگر کنار آدرس سایت دیده نمی شود یا پیام «Not secure» می آید، علت در بیشتر موارد خطای mixed content وردپرس است. سایت با https باز می شود، اما چند فایل داخل صفحه هنوز با http خوانده می شوند. سریع ترین راه حل این است که با DevTools مرورگر آدرس های http را پیدا کنید و با یک Search Replace روی دیتابیس همه را به https ببرید. اگر خود گواهی مشکل دارد و صفحه اصلا باز نمی شود، خطای اتصال امن نیست موضوع دیگری است.

خطای mixed content یعنی چه

مرورگر وقتی صفحه ای را با HTTPS باز می کند، انتظار دارد هر چیزی که داخل آن لود می شود هم از مسیر امن بیاید. اگر یک تصویر، فایل CSS، اسکریپت یا iframe با آدرس http:// صدا زده شود، مرورگر آن را mixed content حساب می کند. اسکریپت و CSS و iframe معمولا بلاک می شوند و صفحه به هم می ریزد. تصویرها ممکن است لود شوند، ولی قفل امن می افتد.

این خطا گواهی SSL را خراب نمی کند. گواهی درست نصب شده و مشکل در آدرس هایی است که داخل محتوا، دیتابیس، قالب یا افزونه ها مانده است.

پیدا کردن آدرس های http

قبل از هر تغییر باید بدانید کدام آدرس ها مشکل دارند. صفحه ای را که قفل ندارد باز کنید، F12 را بزنید و تب Console را ببینید. پیام ها شبیه این هستند:

Mixed Content: The page at 'https://example.com/' was loaded over HTTPS,
but requested an insecure image 'http://example.com/wp-content/uploads/logo.png'.
This content should also be served over HTTPS.

آدرس داخل پیام همان فایلی است که باید اصلاح شود. اگر Console شلوغ است، تب Network را باز کنید و در فیلتر عبارت http: را بنویسید. روش دیگر دستور curl است که آدرس های http را از HTML خروجی بیرون می کشد:

curl -s https://example.com/ | grep -o 'http://[^"'"'"' )]*' | sort -u

این دستور فقط صفحه ای را که می دهید بررسی می کند. صفحه اصلی، یک نوشته، صفحه محصول و صفحه تماس را جداگانه امتحان کنید، چون هر کدام ممکن است از قالب یا افزونه متفاوتی آدرس بخوانند.

علت ها به ترتیب شیوع

علتنشانهراه حل
آدرس های http داخل نوشته ها و تنظیمات در دیتابیسآدرس فایل در wp-content/uploads استSearch Replace روی دیتابیس
آدرس سایت در تنظیمات وردپرس هنوز http استSettings > General آدرس http نشان می دهدتغییر WordPress Address و Site Address
صفحه ساز یا قالب CSS قدیمی را کش کردهآدرس در فایل CSS داخل uploads یا cache استبازسازی CSS و پاک کردن کش
وردپرس پشت پروکسی یا CDN است و HTTPS را تشخیص نمی دهدآدرس های ساخته شده توسط خود وردپرس http هستندتنظیم هدر X-Forwarded-Proto در wp-config.php
فایل خارجی با آدرس httpدامنه ای غیر از سایت شما در پیام کنسول استآدرس را به https تغییر بدهید یا فایل را روی هاست خودتان بگذارید

راه حل قدم به قدم

پیش از هر تغییر در دیتابیس یک بکاپ کامل بگیرید. Search Replace روی کل دیتابیس اجرا می شود و بازگرداندن اشتباه آن بدون بکاپ ممکن نیست. مراحل بکاپ را در آموزش بکاپ و ریستور در cPanel می بینید.

  1. در پیشخوان وردپرس به Settings > General بروید و هر دو فیلد WordPress Address (URL) و Site Address (URL) را با https:// ذخیره کنید. اگر این فیلدها خاکستری و غیرقابل ویرایش هستند، مقدار آن ها در wp-config.php با ثابت های WP_HOME و WP_SITEURL تعریف شده و باید همان جا عوض شود.
  2. آدرس های قدیمی را در دیتابیس عوض کنید. با WP-CLI (روی VPS یا هاستی که Terminal دارد) ابتدا یک اجرای آزمایشی بگیرید و بعد اصلی را اجرا کنید:
wp search-replace 'http://example.com' 'https://example.com' --skip-columns=guid --dry-run
wp search-replace 'http://example.com' 'https://example.com' --skip-columns=guid
  1. اگر Terminal ندارید، افزونه Better Search Replace همین کار را از پیشخوان انجام می دهد. در فیلد Search آدرس http و در Replace آدرس https را بنویسید، همه جدول ها را انتخاب کنید و اول گزینه Run as dry run را بزنید.
  2. کش را پاک کنید: افزونه کش (LiteSpeed Cache، WP Rocket و مشابه آن ها)، کش مرورگر و اگر از Cloudflare استفاده می کنید، Purge Everything.
  3. صفحه را دوباره با DevTools بررسی کنید و تا وقتی پیامی در Console مانده، سراغ همان آدرس بروید.

افزونه ها و قالب ها بعضی تنظیمات را به صورت serialized در دیتابیس ذخیره می کنند و طول رشته آدرس را هم داخل آن می نویسند. اگر آدرس را با ویرایش متنی ساده در یک فایل SQL عوض کنید، این طول ها نادرست می شوند و تنظیمات از بین می روند. WP-CLI و Better Search Replace این مورد را درست مدیریت می کنند، برای همین از ویرایش دستی فایل dump خودداری کنید. توضیح رسمی دستور در مستندات WP-CLI آمده است.

Elementor و صفحه سازهای دیگر

Elementor آدرس ها را داخل فایل های CSS ساخته شده نگه می دارد، پس بعد از Search Replace هم ممکن است آدرس قدیمی باقی بماند. در Elementor از مسیر Elementor > Tools ابتدا گزینه Replace URL را اجرا کنید و بعد در تب General گزینه Regenerate CSS & Data را بزنید. در صفحه سازهای دیگر هم معمولا گزینه ای برای بازسازی فایل های CSS وجود دارد. نام آن به نسخه بستگی دارد، پس در تنظیمات افزونه دنبال عبارت هایی مثل Regenerate یا Clear cache بگردید.

وردپرس پشت پروکسی یا Cloudflare

اگر CDN یا پروکسی بین بازدیدکننده و سرور باشد، ممکن است اتصال به سرور اصلی با HTTP برقرار شود و وردپرس فکر کند سایت روی HTTP است. نتیجه اش این است که آدرس های ساخته شده توسط خود وردپرس http می شوند. در wp-config.php، بالای خط That's all, stop editing، این کد را اضافه کنید:

if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
    $_SERVER['HTTPS'] = 'on';
}

در Cloudflare حالت SSL/TLS را روی Full یا Full (strict) بگذارید. حالت Flexible همین مشکل را می سازد و با ریدایرکت اجباری https معمولا به خطای ریدایرکت بیش از حد هم می رسد.

راه حل موقت با upgrade-insecure-requests

اگر آدرس های http زیاد هستند و فعلا وقت اصلاح ندارید، می توانید از مرورگر بخواهید همه درخواست های http همان صفحه را خودش به https ببرد. در .htaccess ریشه سایت این خط را اضافه کنید:

<IfModule mod_headers.c>
Header always set Content-Security-Policy "upgrade-insecure-requests"
</IfModule>

این روش فقط وقتی کار می کند که فایل روی https هم در دسترس باشد. برای فایل هایی که فقط روی http هستند، همان خطا ادامه پیدا می کند. آن را راه حل نهایی نگیرید و بعد از اصلاح دیتابیس بردارید. افزونه Really Simple SSL هم از همین نوع راه حل ها استفاده می کند و برای شروع قابل قبول است، ولی اگر منبع آدرس ها را درست نکنید، با غیرفعال شدن افزونه مشکل برمی گردد.

آدرس هایی که از قالب و فایل های خارجی می آیند

گاهی بعد از Search Replace هم پیام در Console می ماند. در این حالت آدرس داخل دیتابیس نیست و باید جای دیگر دنبالش بگردید.

فایل های قالب، به خصوص قالب های قدیمی یا ترجمه شده، ممکن است آدرس فونت، اسکریپت آنالیتیکس یا تصویر را با http:// نوشته باشند. با جستجو در پوشه قالب پیدا می شود:

grep -rn "http://" wp-content/themes/نام-قالب --include=*.php --include=*.css --include=*.js

اگر قالب را خودتان نساخته اید، ویرایش مستقیم فایل با اولین به روزرسانی از بین می رود. بهتر است از Child Theme استفاده کنید یا مسئله را به سازنده قالب گزارش بدهید. فایل های JavaScript و CSS که از دامنه دیگری لود می شوند هم باید نسخه https داشته باشند. بیشتر سرویس های بزرگ دارند و کافی است http: را به https: عوض کنید. اگر سرویس فقط روی http در دسترس است، فایل را روی هاست خودتان بگذارید یا جایگزینی پیدا کنید.

دو منبع دیگر که دیر پیدا می شوند: تصویرهای شاخص و ویجت های متنی که در پیشخوان با آدرس کامل اضافه شده اند، و فرم ها یا iframe هایی که کد embed قدیمی دارند. اولی معمولا با Search Replace درست می شود، ولی دومی گاهی داخل تنظیمات خود افزونه ذخیره شده است و باید همان جا ویرایش شود.

در فروشگاه های WooCommerce یک بار هم صفحه پرداخت و صفحه حساب کاربری را با DevTools ببینید، چون افزونه درگاه پرداخت ممکن است آدرس بازگشت یا آیکون را جداگانه بسازد.

برای یک بررسی کلی تر می توانید خروجی curl را برای چند صفحه در یک حلقه بگیرید و فقط آدرس های یکتا را نگه دارید. اگر فهرست خالی بود ولی مرورگر هنوز هشدار می دهد، آدرس از JavaScript ساخته می شود و فقط در DevTools دیده می شود.

چطور مطمئن شویم حل شده

  • در تب Console هیچ پیام Mixed Content باقی نمانده باشد.
  • در تب Network هیچ درخواستی با http: نباشد.
  • صفحه اصلی، نوشته، محصول و صفحه پرداخت را باز کنید. هر قالب صفحه یک بار.
  • ابزار Why No Padlock هم آدرس های ناامن را فهرست می کند، به شرطی که صفحه عمومی باشد.

بعد از رفع مشکل، بررسی کنید که ریدایرکت http به https و www هم درست تنظیم شده باشد. دستور آن در راهنمای ریدایرکت با htaccess آمده است. اگر هنوز بعد از نصب گواهی به https نمی رسید، مشکل صدور AutoSSL در cPanel را بررسی کنید. برای سایتی که تازه منتقل کرده اید، راهنمای انتقال سایت وردپرس هم همین مرحله را در چک لیست خود دارد. پیکوهاست روی هاست های cPanel خودش SSL رایگان صادر می کند، ولی تغییر آدرس های داخل دیتابیس همیشه با خود شما است.