خطای cURL error 28: Operation timed out در وردپرس یعنی یک درخواست خروجی HTTP/HTTPS در مهلت تعیینشده به نتیجه نرسیده است؛ نه اینکه لزوماً SSL خراب باشد یا هاست باید عوض شود. ابتدا مقصد درخواست و زمان DNS، اتصال TCP، TLS و دریافت پاسخ را از همان سرور اندازه بگیرید؛ سپس بر اساس نتیجه، DNS، مسیر خروجی، IPv6، Loopback یا کندی سرویس مقصد را بررسی کنید. طبق مستندات libcurl، کد ۲۸ نشاندهنده رسیدن به زمان انقضاست و با cURL error 60 که به اعتبارسنجی گواهی مربوط است تفاوت دارد.
خطای cURL error 28 کجا دیده میشود و چه معنایی دارد؟
این پیام ممکن است هنگام نصب یا آپدیت افزونه، اتصال به درگاه یا API خارجی، اجرای Cron، بررسی Site Health یا ارسال درخواست REST دیده شود. متنهایی مانند Connection timed out after 10001 milliseconds یا Operation timed out after 5000 milliseconds with 0 bytes received فقط میگویند درخواست در زمان مجاز تمام نشده؛ از روی آنها بهتنهایی نمیتوان DNS، فایروال یا کندی مقصد را مقصر قطعی دانست. در مقابل، اگر متن خطا SSL certificate problem است، مسیر تشخیص در آموزش رفع cURL error 60 و بررسی CA Bundle توضیح داده شده است.
پیشنیازها و نکات ایمنی قبل از عیبیابی
به مدیریت وردپرس و Site Health دسترسی داشته باشید. برای اجرای دستورهای خط فرمان به SSH یا Terminal هاست نیاز دارید؛ اگر هاست اشتراکی چنین دسترسیای نمیدهد، خروجی تستها را از پشتیبانی میزبان درخواست کنید. آدرس دامنه یا API ناموفق، زمان بروز خطا، افزونه مربوط و متن کامل خطا را یادداشت کنید. تغییرات فایروال، DNS یا فایل پیکربندی را بدون نسخه پشتیبان و امکان بازگشت انجام ندهید. کلید API، توکن و کوکی را در گزارش عمومی قرار ندهید.
مرحله اول: مقصد ناموفق و زمان واقعی درخواست را مشخص کنید
در وردپرس به ابزارها ← سلامت سایت بروید و ببینید هشدار مربوط به REST API، Loopback یا ارتباط با WordPress.org است. اگر فقط یک افزونه خطا میدهد، URL مقصد همان افزونه را از لاگ خودش یا مستندات سازنده پیدا کنید؛ درخواست آزمایشی را صرفاً به مقصد مورداعتماد بفرستید. اگر WP-CLI دارید، یک درخواست کنترلشده از داخل HTTP API وردپرس اجرا کنید:
wp eval '$r=wp_remote_get("https://api.wordpress.org/",array("timeout"=>10)); echo is_wp_error($r)?$r->get_error_message():wp_remote_retrieve_response_code($r);'خروجی موفق معمولاً یک کد HTTP مانند 200 یا پاسخ معتبر دیگری از مقصد است؛ پیام cURL error 28 به معنی تداوم Timeout است. کد HTTP 403 یا 404 بهخودیخود Timeout نیست و باید جداگانه بررسی شود. اگر wp در دسترس نیست، همین آزمون را با ابزار تست سازگار با هاست و بدون افزودن افزونه ناشناس انجام دهید.
مرحله دوم: DNS، اتصال و زمان پاسخ را از خود سرور اندازه بگیرید
اجرای تست از لپتاپ شما کافی نیست؛ مسیر خروجی سرور میزبان میتواند متفاوت باشد. در SSH سرور، نام مقصد واقعی را جایگزین کنید. نمونه زیر برای api.wordpress.org است:
getent ahosts api.wordpress.org
curl -sS -o /dev/null -w 'dns=%{time_namelookup} connect=%{time_connect} tls=%{time_appconnect} first_byte=%{time_starttransfer} total=%{time_total}' --connect-timeout 5 --max-time 15 https://api.wordpress.org/در خروجی، dns زمان تبدیل نام، connect زمان رسیدن به اتصال TCP، tls زمان تکمیل TLS، first_byte زمان دریافت نخستین بایت و total زمان کل را نشان میدهد. این زمانها تجمعی هستند، نه زمان جداگانه هر مرحله. اگر getent نتیجه نمیدهد یا مرحله DNS طولانی است، Resolver و تنظیمات DNS سرور را بررسی کنید؛ اگر DNS سریع ولی اتصال کند است، مسیر شبکه، محدودیت اتصال خروجی و دسترسی به پورت ۴۴۳ را بررسی کنید. اگر اتصال و TLS سریع ولی first_byte بالا است، پاسخدهی مقصد یا واسطهایی مثل Proxy/WAF را بررسی کنید.
نکته: ممکن است خروجی curl سیستم با نتیجه HTTP API وردپرس فرق داشته باشد؛ چون نسخه PHP/cURL، Proxy، Resolver و تنظیمات افزونهها میتوانند متفاوت باشند. هر دو مسیر را ثبت و مقایسه کنید.
مرحله سوم: تفاوت مسیر IPv4 و IPv6 را آزمایش کنید
اگر دامنه رکوردهای A و AAAA دارد اما سرور به IPv6 دسترسی سالم ندارد، بعضی درخواستها در مسیر IPv6 معطل میشوند. بدون تغییر تنظیمات عمومی شبکه، هر خانواده IP را جداگانه آزمایش کنید:
curl -4 -I --connect-timeout 5 --max-time 15 https://api.wordpress.org/
curl -6 -I --connect-timeout 5 --max-time 15 https://api.wordpress.org/موفقیت IPv4 و شکست IPv6 یک سرنخ است، نه اثبات اینکه تمام درخواستهای PHP از IPv6 استفاده میکنند. در هاست اشتراکی نتیجه را به پشتیبانی اعلام کنید؛ روی VPS نیز قبل از هر تغییر، Route، Gateway و Resolver را بررسی کنید. غیرفعالکردن سراسری IPv6 بدون تحلیل اثر آن بر سرویسها توصیه نمیشود.
مرحله چهارم: اگر خطا مربوط به Loopback است، مسیر خود سایت را بررسی کنید
درخواست Loopback از خود سرور به دامنه خودش ارسال میشود و ممکن است بهدلیل DNS اشتباه، ریدایرکت، احراز هویت، WAF یا افزونه امنیتی شکست بخورد. ابتدا آدرسهای سایت را ببینید و از همان سرور به دامنه واقعی درخواست بفرستید:
wp option get home
wp option get siteurl
curl -I --connect-timeout 5 --max-time 15 https://example.com/آدرس نمونه را با دامنه واقعی جایگزین کنید. ریدایرکت بیپایان، پاسخ WAF و DNS داخلی اشتباه را از هم جدا کنید. برای بررسی عمیق درخواست بازگشتی، راهنمای رفع Loopback Request Failed در Site Health را ببینید. خاموشکردن دائمی WAF یا دورزدن اعتبارسنجی TLS راهکار قابلقبول نیست؛ برای تست موقت از Rule محدود و قابل بازگشت استفاده کنید.
مرحله پنجم: محدودیت خروجی، منابع PHP و سرویس مقصد را جدا کنید
اگر همه درخواستهای خارجی از همان هاست Timeout میشوند، ابتدا از میزبان درباره اتصال خروجی TCP/443، محدودیت Firewall، DNS Resolver و Proxy سؤال کنید. اگر فقط هنگام بار زیاد سایت رخ میدهد، وضعیت CPU، حافظه، صف PHP-FPM و درخواستهای کند را بررسی کنید. برای شناسایی کد PHP کند، راهنمای بررسی PHP-FPM Slow Log و Stack Trace میتواند کمک کند. کندی سایت الزاماً به معنای کندی API خارجی نیست؛ با زمانبندی درخواستها و لاگها همبستگی را بسنجید.
در هاست اشتراکی، وقتی علت به محدودیتهای مدیریت میزبانی، دسترسی خروجی یا ظرفیت واقعی منابع مربوط میشود، مقایسه پلنهای هاست وانسرور برای انتخاب سطح دسترسی و منابع متناسب مفید است. پیش از تصمیم، از پشتیبانی درباره سیاست اتصال خروجی و امکانات پلن سؤال کنید؛ انتقال هاست بهتنهایی Timeout ناشی از کد افزونه، DNS یا API مقصد را خودکار برطرف نمیکند.
مرحله ششم: اگر فقط یک افزونه یا API Timeout دارد چه کنیم؟
نسخه افزونه، آدرس Endpoint، وضعیت سرویس مقصد و محدودیت Rate Limit را بررسی کنید. مستندات رسمی WP_Http::request() مقدار پیشفرض timeout را ۵ ثانیه اعلام میکند؛ افزونهها ممکن است مقدار متفاوتی تعیین کنند. در کدی که خودتان کنترل میکنید، میتوانید فقط برای درخواست موردنیاز و پس از بررسی علت، Timeout آزمایشی معقولی تعیین کنید:
$response = wp_remote_get(
'https://api.wordpress.org/',
array( 'timeout' => 10 )
);
if ( is_wp_error( $response ) ) {
error_log( $response->get_error_message() );
}این نمونه PHP برای توسعهدهنده است و نباید بدون بررسی در فایلهای اصلی وردپرس قرار بگیرد. افزایش Timeout یک روش تشخیص موقت است، نه درمان DNS خراب، اتصال مسدود یا API ازکارافتاده. افزایش سراسری زمان انتظار میتواند باعث اشغال Workerهای PHP و کندترشدن سایت شود. در Endpointهای حساس از Retry کنترلشده و Backoff مطابق مستندات همان API استفاده کنید.
چگونه رفع خطا را Verify کنیم؟
همان URL و شرایطی را که خطا را بازتولید میکردند دوباره آزمایش کنید. نتیجه موفق باید در چند اجرای متوالی پایدار باشد، نه فقط یک بار. زمانهای dns، connect، first_byte و total را قبل و بعد مقایسه کنید؛ سپس در Site Health و افزونه مشکلدار، نبود خطای ۲۸ را تأیید کنید. اگر پاسخ HTTP دریافت میکنید اما خطای 401/403 یا 5xx باقی است، وارد مسیر عیبیابی همان کد شوید؛ Timeout حل شده ولی مشکل دیگری باقی مانده است.
خطاهای رایج و بازگردانی تغییرات
- اشتباه گرفتن کد ۲۸ با کد ۶۰: اولی Timeout است و دومی معمولاً اعتبارسنجی TLS؛ برای کد ۲۸
sslverify=falseیاcurl -kنگذارید. - تست از دستگاه شخصی: مسیر شبکه لپتاپ الزاماً همان مسیر سرور PHP نیست.
- افزایش بیرویه Timeout: ممکن است فقط نشانه را پنهان کند و Workerهای بیشتری اشغال شوند.
- خاموشکردن دائمی فایروال یا WAF: بهجای آن لاگ و Rule مرتبط را بررسی کنید و استثنای حداقلی بسازید.
- تغییر DNS/IPv6 بدون برنامه بازگشت: مقادیر قبلی و زمان تغییر را ثبت کنید؛ اگر خطا رفع نشد یا سرویس دیگری آسیب دید، تنظیمات را برگردانید.
اگر برای آزمایش، Timeout یک درخواست، Rule فایروال، Resolver یا تنظیمات Proxy را تغییر دادهاید، پس از ثبت نتایج، تغییر آزمایشی را حذف یا به مقدار تأییدشده برگردانید و مجدداً همان تست را اجرا کنید. در هاست اشتراکی، تغییرات زیرساختی باید توسط میزبان و با ثبت درخواست پشتیبانی انجام شوند.
مبنای فنی این راهنما مستندات کدهای خطای libcurl و پارامترهای WordPress HTTP API است؛ نتیجه نهایی هر سایت به لاگ و آزمایش همان زیرساخت وابسته است.