النطاقات الفرعية هي النسخة السهلة من تعدّد المستأجرين: شهادة wildcard تغطّي *.yourapp.com، تقرأ المستأجر من ترويسة المضيف، وانتهيت. يوم يطلب عميل استخدام app.theirbrand.com بدلاً منها، تختفي كل تلك الراحة — لأنك الآن تحتاج شهادة TLS صالحة لنطاق لا تملكه.
شغّلت هذا في الإنتاج، و Cloudflare for SaaS هو الجزء الذي يجعله قابلاً للإدارة. يُصدر ويجدّد الشهادات لنطاقات عملائك فلا تلمس مفتاحاً خاصاً أبداً.
شكل المشكلة
ثلاثة أمور يجب أن تتوافق ليعمل نطاق مخصّص:
- العميل يوجّه نطاقه إليك بسجلّ DNS.
- توجد شهادة TLS لذلك المضيف بالضبط.
- تطبيقك يربط المضيف الوارد بالمستأجر الصحيح.
Cloudflare for SaaS يملك الأولين عبر custom hostnames. وتطبيق Laravel يملك الثالث. إبقاء هذا الخطّ واضحاً هو ما يمنع تحوّله إلى كابوس دعم.
كيف يتدفّق الطلب فعلاً
العميل يضيف CNAME من app.theirbrand.com إلى مضيف تعطيه إياه (يسمّيه Cloudflare الأصل الاحتياطي). يرى Cloudflare الحركة، يتحقّق من الملكية، يوفّر شهادة لـapp.theirbrand.com، ينهي TLS عند حافته، ويمرّر الطلب لأصلك مع ترويسة Host الأصلية سليمة.
تلك التفصيلة الأخيرة هي الحيلة كلها: خادمك يستقبل Host: app.theirbrand.com، وتلك الترويسة هي مفتاح المستأجر.
// middleware يحلّ المستأجر من أي مضيف وصل
class IdentifyTenant
{
public function handle(Request $request, Closure $next)
{
$host = $request->getHost();
$tenant = Tenant::where('primary_domain', $host)
->orWhere('custom_domain', $host)
->firstOrFail();
app()->instance('tenant', $tenant);
return $next($request);
}
}
توفير نطاق دون تدخّل بشري
حين يحفظ عميل نطاقاً مخصّصاً في لوحته، لا أراسل أحداً. أنادي واجهة custom hostnames في Cloudflare، أخزّن سجلات التحقّق التي تعيدها، وأعرضها للعميل ليضيفها إلى DNS الخاص به.
$response = Http::withToken(config('services.cloudflare.token'))
->post("https://api.cloudflare.com/client/v4/zones/{$zone}/custom_hostnames", [
'hostname' => $tenant->custom_domain,
'ssl' => ['method' => 'http', 'type' => 'dv'],
]);
$tenant->update([
'cf_hostname_id' => $response->json('result.id'),
'domain_status' => 'pending',
]);
ثم يتولّى Cloudflare التحقّق وإصدار الشهادة بنفسه. أراقب تحوّل الحالة إلى active — إمّا باستطلاع معرّف المضيف دورياً أو عبر webhook — وعندها فقط أعتمد النطاق في اللوحة. يرى العميل "pending → active" ولا يعلم أن الأمر تعلّق بشهادات أصلاً.
التفاصيل التي تلسعك في الإنتاج
دروس كلّفتني وقتاً كي لا تكلّفك:
- يجب إعداد الأصل الاحتياطي قبل أن يخدم أي مضيف مخصّص حركة. إعداد لمرة واحدة، ونسيانه يجعل كل نطاق يبدو معطّلاً لأسباب ليست في سجلّات تطبيقك.
- خزّن نتيجة ربط المضيف بالمستأجر مؤقتاً. يعمل مع كل طلب؛ واستعلام بارد لكل طلب يتراكم بسرعة. تخزين قصير العمر بمفتاح المضيف يردّ تكلفته فوراً.
- عامل دورة حياة النطاق كآلة حالات لا قيمة منطقية.
pendingوactiveوfailedوdeleted— كلّ يحتاج معالجته، و"هل تم التحقّق؟" سؤال ستجيبه آلاف المرات. - خطّط للإزالة. حين يرحل عميل، احذف المضيف المخصّص عبر الواجهة أيضاً، وإلّا تركت شهادات يتيمة ومضيفاً ما زال يشير إليك.
حين يُنفّذ جيداً، تجربة العميل تكاد تكون بلا إثارة: يلصق نطاقه، يضيف سجلّ DNS واحداً، ينتظر دقيقة، وتظهر علامته التجارية على منصّتك عبر HTTPS. كل الأجزاء الصعبة — التحقّق من الملكية، إصدار الشهادة، التجديد للأبد — تحدث حيث تنتمي، ويبقى تطبيقك Laravel نظيفاً متعدّد المستأجرين يقرأ ترويسة مضيف فحسب.