حل مشكلة 403 Forbidden في Laravel: دليل التشخيص خطوة بخطوة

شروحات تقنية

قبل أن تبدأ بتعديل الأذونات أو ملف .htaccess، اعرف أولًا أن خطأ 403 Forbidden في Laravel له مصدران مختلفان تمامًا، والحل يختلف جذريًا بينهما: إما أن الخادم (Apache/Nginx) يرفض تسليم الملف قبل أن يعمل Laravel أصلًا، أو أن Laravel نفسه يشتغل بشكل سليم لكنه يعيد 403 عمدًا لأن منطق التفويض (Gate/Policy/Middleware) منع المستخدم. الطريقة الأسرع للتفريق: لو رأيت صفحة 403 بتصميم Apache/Nginx الافتراضي، فالمشكلة في الخادم؛ ولو رأيت صفحة خطأ Laravel المنسّقة (أو استجابة JSON فيها "message")، فالمشكلة داخل التطبيق. هذا التمييز وحده يوفّر عليك ساعات من البحث في المكان الخطأ.

متى يكون الخطأ من الخادم (قبل تشغيل Laravel)؟

هذا النوع يظهر عادة عند نشر المشروع على استضافة جديدة. المستخدم يطلب الرابط، فيرفض الخادم فورًا. أهم الأسباب وحلولها:

  1. جذر الموقع (document root) لا يشير إلى مجلد public. هذا السبب الأول والأكثر شيوعًا. Laravel يجب أن يُخدَّم من داخل public/ فقط، وليس من جذر المشروع. عدّل إعداد الاستضافة أو الـ VirtualHost بحيث يكون الجذر هو /path/to/project/public. لو كنت على استضافة مشتركة، أنشئ رابطًا رمزيًا أو انقل محتوى public بحذر مع تعديل مسارات index.php.

  2. تعطيل mod_rewrite في Apache. لو كان mod_rewrite غير مفعّل أو AllowOverride مضبوطًا على None، فلن يُقرأ ملف .htaccess وقد تحصل على 403. فعّل الوحدة بـ a2enmod rewrite واضبط AllowOverride All داخل كتلة <Directory> ثم أعد تشغيل Apache.

  3. إعداد Nginx بدون try_files. على Nginx تأكد أن كتلة location / تحتوي على try_files $uri $uri/ /index.php?$query_string;، وأن مسار root ينتهي بـ /public.

  4. أذونات أو ملكية خاطئة. يجب أن يملك مستخدم خادم الويب (www-data غالبًا) صلاحية القراءة، وأن يملك صلاحية الكتابة على storage وbootstrap/cache تحديدًا.

متى يكون الخطأ من Laravel نفسه (التفويض)؟

هنا التطبيق يعمل تمامًا، لكنه يقرر رفض الوصول. مصادر 403 داخل الكود:

  • Gate أو Policy تُعيد false. أي استدعاء مثل Gate::authorize() أو $this->authorize('update', $post) أو $user->can('update', $post) سيطلق استثناء AuthorizationException يترجَم إلى 403 عندما لا يملك المستخدم الصلاحية.
  • Middleware للتفويض في المسار. وسيط مثل ->middleware('can:update,post') أو وسيط أدوار كـ role:admin يعيد 403 عند الفشل.
  • استدعاء صريح لـ abort(403) في متحكم أو وسيط كتبته أنت.

لتشخيص هذا النوع: افتح storage/logs/laravel.log، وابحث عن آخر AuthorizationException. سيخبرك بالسياسة (Policy) والدالة (ability) التي رفضت. غالبًا يكون السبب أن المستخدم غير مسجّل الدخول، أو أن دالة الـ Policy تقارن $user->id بحقل خاطئ، أو أنك نسيت تسجيل الـ Policy في AuthServiceProvider.

جدول: خطأ الخادم أم خطأ Laravel؟

العلامة403 من الخادم403 من Laravel
شكل الصفحةتصميم Apache/Nginx افتراضيصفحة Laravel منسّقة أو JSON
يظهر في سجل laravel.log؟لانعم (AuthorizationException)
متى يحدث غالبًا؟بعد النشر مباشرةبعد تسجيل الدخول أو على مسار محمي
مكان الإصلاحإعداد الخادم والأذوناتPolicy / Gate / Middleware
يتأثر بالمستخدم؟لا، يظهر للجميعنعم، حسب صلاحية المستخدم

خطوات التشخيص السريع

  1. افتح أدوات المطوّر في المتصفح (تبويب Network) وانظر لرأس الاستجابة Server وشكل جسم الرسالة لتحدد المصدر.
  2. راجع storage/logs/laravel.log؛ وجودُ استثناء يعني أن Laravel وصل ونفّذ الطلب.
  3. لو لم يصل الطلب إلى Laravel، راجع سجلات الخادم: /var/log/apache2/error.log أو /var/log/nginx/error.log.
  4. تأكد أن الجذر يشير إلى public، وأن .htaccess الأصلي الخاص بـ Laravel موجود داخل public/.
  5. لو المشكلة تفويضية، تتبّع الـ Policy المسؤولة وتحقق من تسجيل الدخول وحالة $user.

خطأ شائع يجب تجنّبه

الطريقة التي تنتشر في المقالات القديمة — تنفيذ chmod -R 644 على كامل المشروع — خطأ يكسر الموقع، لأن المجلدات تحتاج صلاحية التنفيذ (x) كي تُفتَح، فتحويلها إلى 644 يسبب 403 بدل أن يحلّه. الصحيح فصلُ المجلدات عن الملفات:

find . -type d -exec chmod 755 {} \;
find . -type f -exec chmod 644 {} \;

ولا تلجأ أبدًا إلى chmod -R 777 كحل سريع؛ فهو ثغرة أمنية خطيرة على أي خادم إنتاج. المطلوب فقط أن تكون storage وbootstrap/cache قابلة للكتابة من مستخدم خادم الويب.

الأسئلة الشائعة

لماذا يظهر 403 على الاستضافة المشتركة رغم أن المشروع يعمل محليًا؟ غالبًا لأن جذر النطاق يشير إلى مجلد المشروع بدل public، أو لأن AllowOverride معطّل فلا يُقرأ .htaccess. أنشئ رابطًا رمزيًا للجذر إلى public أو راجع الدعم الفني للاستضافة.

كيف أعرف أن السبب تفويض وليس الخادم؟ لو ظهر الاستثناء في laravel.log فالسبب تفويضي، ولو كانت الصفحة بتصميم الخادم الافتراضي ولا شيء في سجل Laravel فالسبب في إعداد الخادم.

عطّلت الجدار الناري ولم يُحلّ الخطأ، ماذا أفعل؟ الجدار الناري نادرًا ما يسبب 403 (يسبب عادة انقطاعًا أو مهلة). ابدأ بجذر public والأذونات وسجلات الخطأ قبل لمس الجدار الناري.

أرى 403 عند استدعاء API بصيغة JSON فقط، لماذا؟ هذا مؤشر واضح على تفويض داخل Laravel؛ راجع الوسيط can: أو استدعاء authorize() في المتحكم، وتأكد من إرسال رمز المصادقة (Token) الصحيح في رأس الطلب.