نشر Laravel على InfinityFree مجاناً مع MySQL وبدون SSH
دليل نشر Laravel على استضافة InfinityFree المجانية مع MySQL: ما يعمل وما لا يعمل، وتجهيز المشروع محلياً، والرفع بـ FTP، وإعداد .htaccess.
استضافة InfinityFree مجانية وتدعم PHP وMySQL، لكنها ليست خادماً تتحكم فيه: لا SSH ولا Composer ولا artisan على الخادم، وجذر الموقع مجلد ثابت اسمه htdocs. ومع ذلك يمكن تشغيل تطبيق Laravel عليها بشرط أن تفهم القيود وتحضّر كل شيء على جهازك قبل الرفع. هذا الدليل يشرح كل خطوة ولماذا تُفعل بهذه الطريقة، ومتى يجب أن تتوقف عن استخدام هذه الاستضافة.
هذا المقال الثاني من سلسلة. الأول عن النشر على Render مع Neon وDocker. إن كنت تحتاج مهام مجدولة أو queue workers أو API فاقرأ الأول أو فكّر بخادم VPS.
أولاً: ماذا تقدم لك InfinityFree وما حدودها؟
بناءً على شروط الاستخدام في منتدى InfinityFree والدليل المنشور في المنتدى لتثبيت Laravel:
ما تحصل عليه: PHP وMySQL وphpMyAdmin ومساحة رفع بالـ FTP.
ما لا يعمل (لأنه غير متاح في الخطة المجانية):
| الميزة | الوضع | ما يعنيه لتطبيق Laravel |
|---|---|---|
| SSH / Terminal | غير متاح | لا artisan migrate، لا composer install على الخادم |
| Cron jobs | غير متاحة | لا schedule:run |
| Queue workers | غير متاحة | QUEUE_CONNECTION=sync فقط (المهام تُنفَّذ داخل الطلب) |
| WebSockets | غير مدعومة | لا Reverb ولا Echo |
| Redis / Memcached | غير متاحين | الكاش والجلسات عبر file أو database |
| Symlinks | غير مدعومة | لا يعمل storage:link، وسنستبدله بقاعدة rewrite |
| اتصال MySQL من خارج الاستضافة | غير مسموح في الخطة المجانية | لا يمكنك الاتصال بقاعدة البيانات من جهازك ولا تشغيل ترحيلات عن بعد (توضيح الدعم) |
| تغيير جذر الموقع | غير ممكن | الجذر هو htdocs دائماً، وسنحتاج .htaccess |
قيود الاستخدام: موارد المعالج والذاكرة وعدد الطلبات (hits) محدودة بأرقام لا تنشرها الاستضافة، والمساحة محدودة بعدد الملفات (inodes) أيضاً، ومجلد vendor الضخم قد يقترب منها. ويمنع شرط الاستخدام استضافة "الـ API وقواعد البيانات" كخدمة بحد ذاتها، وتخزين الملفات والنسخ الاحتياطية، وسكربتات الدردشة الحية. أي أن الاستضافة مخصصة لـ مواقع عادية، لا لتشغيل API يستهلكه تطبيق آخر. والاستضافة تضع نظام حماية يسمح للمتصفحات العادية فقط، فقد تُحجب أدوات مثل webhooks وطلبات cURL الآلية، فاختبر أي ميزة تعتمد عليها.
خلاصة: مناسبة لموقع تعريفي أو مدونة أو لوحة بسيطة ولتجربة المشروع، وغير مناسبة لنظام يحتاج مهام خلفية أو حمل عالٍ.
ثانياً: تأكد من إصدار PHP
كل إصدار من Laravel يتطلب حداً أدنى من PHP. افتح composer.json وانظر إلى require.php في مشروعك، وتحقق من أن الاستضافة تدعمه.
بحسب إعلان في منتدى InfinityFree تم ترقية الاستضافة المجانية إلى PHP 8.4، لكن إصدار حسابك قد يختلف، فلا تعتمد على الإعلان وحده:
- من لوحة التحكم افتح Select PHP Version (أو ما يماثلها في لوحتك) وانظر ما يتوفر.
- ارفع ملفاً مؤقتاً
phpinfo.phpفيه<?php phpinfo();داخلhtdocs/publicوافتحه، وتأكد من الإصدار ومن وجود الإضافات:pdo_mysqlوmbstringوopensslوfileinfoوgdوintl. احذف الملف بعد الاستخدام لأنه يكشف إعدادات الخادم.
والأهم: ولّد الحزم على جهازك بإصدار PHP مطابق لإصدار الاستضافة. إن ولّدت composer.lock على PHP أعلى من الموجود على الاستضافة فقد تُثبَّت حزم لا تعمل هناك. تأكد قبل الرفع:
composer config platform.php 8.3 # ضع إصدار استضافتك
composer update --no-dev --lock # يعيد حل الحزم على هذا الإصدار
ثالثاً: أنشئ الحساب وقاعدة البيانات
- سجّل في InfinityFree وأنشئ Hosting Account واختر نطاقاً فرعياً مجانياً أو نطاقك الخاص.
- من لوحة التحكم (Control Panel) ستجد بيانات FTP: المضيف واسم المستخدم وكلمة المرور (وهي بيانات الـ FTP لا بيانات حسابك).
- افتح MySQL Databases وأنشئ قاعدة. ستحصل على:
- اسم القاعدة (مسبوق ببادئة حسابك).
- اسم المستخدم وكلمة المرور.
- المضيف (Host): اسم خادم خاص مثل
sqlXXX.infinityfree.com، وهو ليسlocalhost. انسخه كما هو من اللوحة.
- زر phpMyAdmin بجانب القاعدة هو أداتك الوحيدة لإدارتها، لأنك لا تستطيع الاتصال من خارج الاستضافة.
ما قواعد البيانات المتاحة؟ وكم يمكنك إنشاء منها؟
هذه نقطة تحيّر كثيرين، فلنوضحها:
| السؤال | الجواب |
|---|---|
| ما نوع قواعد البيانات المتوفرة؟ | MySQL فقط (مراجعات مستقلة تذكر MySQL 8.0 أو MariaDB 10.6). لا أجد أي مصدر يذكر دعم PostgreSQL على الاستضافة المجانية، ولا يوجد SQLite كخدمة قاعدة بيانات. وللتأكد من نسخة خادمك افتح phpMyAdmin وانظر إصدار الخادم في الصفحة الرئيسية |
| هل يمكن إنشاء أكثر من قاعدة؟ | نعم. من MySQL Databases في لوحة التحكم تنشئ عدة قواعد، كل واحدة باسم مسبوق ببادئة حسابك. تذكر مراجعات مستقلة سقفاً يصل إلى 400 قاعدة لكل حساب، لكن هذا الرقم ليس من الموقع الرسمي، فاعتمد على ما تعرضه لوحتك |
| ما حجم القاعدة؟ | ذكرت إجابة منسوبة للدعم في المنتدى أنه لا يوجد حد لمساحة قاعدة البيانات في الخطة المجانية، وأن ما يُراقَب هو الحمل الذي تضعه على خوادم القواعد، فالاستعلامات البطيئة وغير المحسّنة قد تسبب مشاكل. فلا تعتمد على هذا للتحميل الثقيل |
| كيف أدير القواعد؟ | عبر phpMyAdmin فقط (استيراد/تصدير وتنفيذ SQL)، أو عبر كود PHP على نفس الاستضافة |
| هل يمكن الوصول إليها من جهازي؟ | لا في الخطة المجانية. الاتصال يتم من داخل الاستضافة فقط (توضيح) |
| هل يمكن لتطبيقي على InfinityFree استخدام قاعدة خارجية (مثل Neon أو غيره)؟ | غير مؤكد. مستخدمون في المنتدى يذكرون أن الاتصالات الخارجية كانت تعمل ثم حُجبت، ولم أجد ردّاً رسمياً (نقاش). وحتى لو سمحت الشبكة فلن تستطيع استخدام Postgres/Neon دون إضافة pdo_pgsql، ولا دليل على توفرها. فاعتبر قاعدة MySQL المرفقة هي الخيار العملي، وجرّب أي اتصال خارجي بسكربت صغير قبل أن تبني عليه |
متى تحتاج أكثر من قاعدة؟
- لكل تطبيق قاعدته: إن استضفت أكثر من موقع Laravel على الحساب نفسه، أنشئ لكل واحد قاعدة مستقلة بمستخدمها حتى لا تتداخل البيانات.
- بيئات منفصلة: قاعدة للتجربة (staging) وأخرى للإنتاج، وبدّل بينهما بـ
.env. - تقليل الحمل: لا حاجة لتوزيع جداول تطبيق واحد على عدة قواعد، فهذا يعقّد الاستعلامات والعلاقات بلا فائدة. ما دمت تحت حدود الحمل، قاعدة واحدة تكفي.
وإن أردت فعلاً أن يتصل تطبيق واحد بقاعدتين على الاستضافة نفسها (مثلاً قاعدة للتقارير) فعرّف اتصالاً ثانياً في config/database.php:
'connections' => [
'mysql' => [ /* الاتصال الأساسي كما هو، من DB_* */ ],
'mysql_reports' => [
'driver' => 'mysql',
'host' => env('DB2_HOST'),
'port' => env('DB2_PORT', '3306'),
'database' => env('DB2_DATABASE'),
'username' => env('DB2_USERNAME'),
'password' => env('DB2_PASSWORD'),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
],
],
واستخدمه في الموديل بـ protected $connection = 'mysql_reports';، أو في استعلام بـ DB::connection('mysql_reports')->table(...). وتذكّر أن بيانات اتصال كل قاعدة تأتي من لوحة MySQL Databases (كل قاعدة بمستخدمها وكلمة مرورها).
النسخ الاحتياطي: لأنك لا تستطيع تشغيل mysqldump ضد الخادم من جهازك، فخذ نسخك من phpMyAdmin ← Export بانتظام وخزّنها خارج الاستضافة. ولا تعتمد على الاستضافة المجانية في الاحتفاظ ببياناتك.
رابعاً: التحضير على جهازك (أهم مرحلة)
بما أن الخادم لا يشغّل أي أمر، فكل ما يحتاج أوامر يحدث عندك:
# 1) حزم الإنتاج فقط، مع autoloader محسّن
composer install --no-dev --optimize-autoloader
# 2) بناء الأصول (Vite) فيتولد public/build
npm ci && npm run build
# 3) تنظيف أي كاش محلي (مهم! انظر الشرح)
php artisan optimize:clear
# 4) مفتاح التطبيق (انسخه لملف .env الإنتاجي)
php artisan key:generate --show
شرح كل خطوة:
--no-dev: حزم الاختبار والتطوير لا تلزم في الإنتاج، وتقلل عدد الملفات (inodes) وحجم الرفع.npm run build: الاستضافة لا تشغّل Node. فملفات CSS/JS الجاهزة فيpublic/buildيجب أن تُبنى عندك وتُرفع. وبدونها تحصل على خطأVite manifest not found.optimize:clearوليسoptimize: لا تشغّلconfig:cacheولاroute:cacheعلى جهازك قبل الرفع. الكاش المولّد يحتوي مسارات مطلقة من جهازك (مثل/home/user/project/...) فيتعطل التطبيق على الاستضافة. ننظّف الكاش لا نبنيه.- لا ترفع
node_modules: آلاف الملفات تستهلك حصة الملفات بلا فائدة (ينبه الدليل المنشور لهذا). - مفتاح التطبيق:
APP_KEYيشفّر الجلسات والـ cookies. ولّده مرة واحدة واحفظه، فتغييره لاحقاً يبطل الجلسات المشفرة.
ملف .env للإنتاج
أنشئ ملفاً جديداً بقيم الإنتاج (لا تنسخ .env المحلي كما هو):
APP_NAME="اسم موقعك"
APP_ENV=production
APP_KEY=base64:................
APP_DEBUG=false
APP_URL=https://yoursite.infinityfreeapp.com
DB_CONNECTION=mysql
DB_HOST=sqlXXX.infinityfree.com
DB_PORT=3306
DB_DATABASE=if0_XXXXXXXX_dbname
DB_USERNAME=if0_XXXXXXXX
DB_PASSWORD=كلمة_المرور
SESSION_DRIVER=file
CACHE_STORE=file
QUEUE_CONNECTION=sync
LOG_CHANNEL=daily
FILESYSTEM_DISK=public
(استبدل الأسماء بما تعرضه لوحتك بالضبط). ملاحظات:
APP_DEBUG=falseإلزامي. إذا بقيتtrueفسيعرض الموقع مساراتك وأسرارك في صفحات الأخطاء.APP_URLبـhttps://: حتى يولّد Laravel روابط آمنة.- الجلسات والكاش على
file: لا Redis. ويمكنك استخدامdatabaseللجلسات بشرط إنشاء جدولها. QUEUE_CONNECTION=sync: هو الخيار الوحيد العملي.- البريد: استخدم مزود بريد خارجياً (SMTP أو API). تأكد من اختباره، لأن بعض الاستضافات المجانية تقيّد الاتصالات الخارجية.
خامساً: إنشاء الجداول بدون artisan migrate
لا يوجد SSH، ولا يمكنك توجيه artisan migrate من جهازك إلى قاعدة الاستضافة (الاتصال الخارجي غير مسموح). لذلك أمامك طريقتان:
الطريقة 1 (الموصى بها): تصدير SQL واستيراده
- أنشئ قاعدة MySQL فارغة محلياً وشغّل عليها:
php artisan migrate --force
php artisan db:seed --force # إن كان عندك seeders ضرورية
- صدّرها:
mysqldump -u root -p --no-tablespaces اسم_القاعدة_المحلية > dump.sql
- في phpMyAdmin على الاستضافة: اختر قاعدتك ← Import ← ارفع
dump.sql.
ملاحظات: إن كان الملف كبيراً فاضغطه (phpMyAdmin يقبل .zip و.gz عادةً)، وانظر الحد الأقصى المعروض في صفحة Import. وكل تغيير لاحق في البنية يتطلب تصدير الجزء المتغير فقط واستيراده أو تنفيذ SQL يدوياً. ويمكنك أيضاً توليد SQL للبنية بـ php artisan schema:dump ثم استيراده كما ذكر الدليل المنشور في المنتدى.
الطريقة 2 (مؤقتة وخطرة): مسار ويب يشغّل الترحيل
أحياناً تحتاج تشغيل ترحيل بعد النشر. يمكنك إضافة مسار مؤقت محمي بسر طويل:
// routes/web.php ← مؤقت، احذفه مباشرة بعد الاستخدام
Route::get('/_run-migrations/{token}', function (string $token) {
abort_unless(hash_equals((string) env('MIGRATE_TOKEN'), $token), 404);
Artisan::call('migrate', ['--force' => true]);
return '<pre>'.e(Artisan::output()).'</pre>';
});
ضع MIGRATE_TOKEN في .env بقيمة عشوائية طويلة (32 حرفاً فأكثر)، افتح الرابط مرة واحدة، ثم احذف المسار والمتغير فوراً. ترك مسار كهذا مفتوحاً يعني أن أي شخص يخمّن السر يستطيع تعديل قاعدة بياناتك. وتأكد أيضاً أنك لم ترفع ملفات كاش الإعدادات من جهازك (bootstrap/cache/config.php) لأنها تحمل مسارات جهازك.
مشاكل شائعة في الاستيراد والترحيل
Specified key was too long: مع إصدارات MySQL القديمة، أضف فيAppServiceProvider::boot():Schema::defaultStringLength(191);ثم أعد توليد القاعدة.- اختلاف الـ collation: استخدم
utf8mb4في كل الجداول لدعم العربية والرموز التعبيرية. - ترتيب الجداول والمفاتيح الأجنبية: الاستيراد من
mysqldumpيضع أوامر إيقاف فحص المفاتيح تلقائياً، فلا تقلق، لكن إن كتبت SQL بيدك فاحرص على الترتيب.
سادساً: بنية المجلدات و.htaccess
بما أن جذر الموقع هو htdocs ولا يمكن تغييره، وأمان Laravel يعتمد على أن يكون الجذر هو مجلد public فقط، فنحتاج حيلة: نضع المشروع كله داخل htdocs ونجعل .htaccess يوجّه كل الطلبات إلى public.
البنية بعد الرفع
htdocs/
├── .htaccess ← الذي سننشئه (يوجّه إلى public)
├── .env
├── app/
├── bootstrap/
├── config/
├── database/
├── public/
│ ├── .htaccess ← الأصلي من Laravel (لا تعدّله)
│ ├── index.php
│ └── build/ ← أصول Vite المبنية
├── resources/
├── routes/
├── storage/
└── vendor/
ملف htdocs/.htaccess
هذه الصيغة من الدليل المنشور في منتدى InfinityFree (بعد تصحيح خطأ فيه):
RewriteEngine On
RewriteRule ^storage/(.*)$ /storage/app/public/$1 [END]
RewriteRule (.*) /public/$1 [L]
شرح السطور:
RewriteEngine On: يفعّل محرك إعادة الكتابة في Apache.- القاعدة الأولى بديل
storage:link: الطلب/storage/photo.jpgيُخدَّم منstorage/app/public/photo.jpgمباشرة. نحتاجها لأن الروابط الرمزية غير مدعومة. يجب أن تسبق القاعدة العامة، وإلا تأكلها الثانية. ولا تلزمك إن كنت لا تستخدم القرصpublic. - القاعدة الثانية: كل طلب آخر يُعاد توجيهه داخلياً إلى
public/. بهذا تصلindex.phpبالطريقة المعتادة، وفي الوقت نفسه تمنع الوصول إلى الملفات الحساسة: الطلب/.envيتحول إلى/public/.envغير الموجود فيرجع 404.
اختبر الأمان فوراً بعد الرفع
افتح هذه الروابط، ويجب أن ترى 404 أو 403 في كلها:
https://موقعك/.envhttps://موقعك/storage/logs/laravel.loghttps://موقعك/composer.jsonhttps://موقعك/vendor/autoload.php
إن ظهر أي محتوى فملف .htaccess لا يعمل، فأوقف الموقع وأصلح الأمر قبل أي شيء، لأن كشف .env يعني كشف كلمات مرور قاعدة البيانات ومفتاح التطبيق.
سابعاً: الرفع بالـ FTP
- استخدم FileZilla (أو أي عميل FTP). في الإعدادات فعّل Show hidden files (Server ← Force showing hidden files) وإلا لن ترى
.envو.htaccessولن تُرفع. - اتصل بمضيف FTP وبياناته من اللوحة، وادخل مجلد
htdocs. - ارفع المحتوى كما في البنية أعلاه. رفع
vendorيستغرق وقتاً لكثرة ملفاته، فاصبر ولا تقطع الرفع. وبعده راجع أن عدد الملفات على الخادم يطابق عدد ملفاتك المحلية. - انتبه: يحذّر الدليل المنشور من أن ملفات PHP التي يتجاوز حجمها 1MB قد تُرفض دون أي رسالة خطأ، فتظهر لاحقاً أخطاء classes مفقودة. إن حصل ذلك فشغّل محلياً
composer dump-autoload --no-dev --optimize=falseوأعد رفع الملفات المتأثرة. - تأكد أن مجلدي
storageوbootstrap/cacheموجودان (حتى لو فارغين) وقابلان للكتابة. إن ظهرت أخطاء صلاحيات فاضبط الأذونات إلى 755 (أو 775) من عميل FTP.
ثامناً: شهادة SSL
من لوحة التحكم يمكنك طلب شهادة SSL مجانية لنطاقك. اتبع الخطوات التي تعرضها اللوحة، وقد يستغرق التفعيل بعض الوقت. بعد تفعيلها:
- اجعل
APP_URL=https://.... - أضف في
AppServiceProvider::boot():
use Illuminate\Support\Facades\URL;
if (app()->environment('production')) {
URL::forceScheme('https');
}
هكذا تُولَّد كل الروابط (CSS وJS وغيرها) بـ https فلا تحصل على خطأ Mixed Content.
تاسعاً: ما لا يعمل وبدائله
| الحاجة | البديل على InfinityFree |
|---|---|
مهام مجدولة (schedule:run) | لا يوجد cron. جرّب خدمة cron خارجية (مثل cron-job.org) تستدعي مساراً محمياً، لكن حماية المتصفح قد تحجب الطلبات الآلية، فاختبر |
| إرسال إيميلات كثيرة | مزود خارجي بـ API/SMTP، وأرسل داخل الطلب (sync) بحذر لأن المهلة قصيرة |
| رفع ملفات كبيرة | تخزين خارجي (S3/R2) إن سمحت الاستضافة بالاتصال به، وإلا فالقرص المحلي بحدود صغيرة |
| الأداء | ركّز على الكاش (file) وتقليل الاستعلامات، وتجنب الحزم الثقيلة |
| الجلسات | file أو database |
عاشراً: سير عمل التحديثات
كل تحديث للكود = إعادة تحضير ورفع. للتنظيم:
- عدّل محلياً واختبر.
composer install --no-dev --optimize-autoloader(إذا تغيرت الحزم) وnpm run build(إذا تغيرت الأصول).php artisan optimize:clear.- ارفع الملفات المتغيرة فقط (FileZilla يظهر الاختلافات)، ولا تعيد رفع
vendorإن لم تتغير الحزم. - إن تغيرت البنية فاستورد SQL التغيير في phpMyAdmin.
- امسح ملفات الكاش على الخادم إن ظهر سلوك لا يطابق الكود: محتويات
bootstrap/cacheوstorage/framework/views(بدون حذف المجلدات نفسها).
ويمكنك أتمتة الرفع بـ GitHub Actions عبر حزمة تدعم FTP مثل SamKirkland/FTP-Deploy-Action (تأكد من اسم مجلد الوجهة server-dir وفق ما تراه في عميل FTP، وخزّن بيانات FTP في GitHub Secrets، ولا تضعها في الملف أبداً).
حادي عشر: أخطاء شائعة وحلولها
| العرض | السبب المحتمل | الحل |
|---|---|---|
| 500 Internal Server Error بلا تفاصيل | خطأ PHP أو .htaccess | أنشئ .env صحيحاً، وافحص storage/logs/laravel.log، وفعّل APP_DEBUG=true مؤقتاً ثم أعده false |
No application encryption key has been specified | APP_KEY فارغ أو .env لم يُرفع | تأكد من رفع .env (إظهار الملفات المخفية) وأن المفتاح فيه |
Class not found | ملف PHP تجاوز 1MB ولم يُرفع، أو autoloader قديم | أعد توليد autoloader وأعد الرفع |
| صفحة بيضاء أو سلوك قديم | كاش قديم مرفوع | امسح bootstrap/cache وstorage/framework/views |
SQLSTATE[HY000] [1045] Access denied | بيانات القاعدة خاطئة | راجع المضيف والمستخدم وكلمة المرور من لوحة MySQL |
SQLSTATE[HY000] [2002] | المضيف خاطئ (استخدمت localhost أو 127.0.0.1) | استخدم اسم المضيف الخاص من اللوحة |
| كل الصفحات 404 ما عدا الرئيسية | public/.htaccess ناقص أو mod_rewrite غير مفعل بسبب ملف ناقص | أعد رفع public/.htaccess الأصلي |
| الصور المرفوعة لا تظهر | لا يوجد storage:link | أضف قاعدة storage في .htaccess الجذري |
| Mixed Content | الصفحة https والأصول http | URL::forceScheme('https') وAPP_URL بـ https |
ملف .env أو .htaccess غير موجود بعد الرفع | العميل يخفي الملفات | فعّل إظهار الملفات المخفية وأعد الرفع |
متى تتركها؟
انتقل لاستضافة أخرى (مثل Render في المقال الأول، أو VPS) إذا احتجت: مهاماً مجدولة، أو queue workers، أو WebSockets، أو API يستهلكه تطبيق آخر، أو وصولاً مباشراً لقاعدة البيانات من أدواتك، أو حملاً أعلى مما تتحمله حدود الاستضافة.
الخلاصة
- InfinityFree تصلح لمواقع Laravel الخفيفة، لكن بلا SSH ولا cron ولا queue ولا اتصال خارجي بقاعدة MySQL.
- كل شيء يُحضَّر محلياً:
composer install --no-devوبناء Vite وoptimize:clearثم الرفع. - جذر الموقع
htdocsثابت، فنضع المشروع داخله ونوجّه الطلبات إلىpublicبـ.htaccess، ثم نختبر أن/.envلا يُفتح. - الجداول تُنشأ عبر تصدير SQL واستيراده بـ phpMyAdmin، لا بـ
artisan migrate. - قواعد البيانات المتاحة MySQL فقط، ويمكنك إنشاء عدة قواعد من اللوحة، لكن لا تصلها من جهازك ولا يُؤكَّد أن بإمكانك الاتصال بقاعدة خارجية (Postgres/Neon غير عملي هنا).
- اعرف حدودها مبكراً: إن كبر مشروعك فالانتقال لاستضافة بصلاحيات أوسع أرخص من محاربة القيود.