تطبيقكم يعمل على localhost. والآن تريدون مشاركته عبر نطاق حقيقي دون إبقاء حاسوبكم المحمول قيد التشغيل. القطع الناقصة هي: VPS، وعملية تطبيق مُدارة، وDNS، وخادم وكيل عكسي (reverse proxy) يوفّر HTTPS.
يعرض هذا الدليل هذا المسار بتطبيق Node.js صغير. يمكن لـ Claude Code مساعدتكم في فحص مشروعكم وإعداد ملفات الإعدادات لمراجعتها، لكنكم تظلون مسؤولين عن التغييرات على الخادم. النموذج غير مثبَّت على VPS، كما أن الوصول إلى Claude وفوترته منفصلان عن الاستضافة.
شرح متحرك. لوحة المعلومات المعروضة وهمية لأغراض العرض التوضيحي، وليست مراقبة فعلية لبنية تحتية حية.
شاهدوا نظرة عامة على عملية النشر
شاهدوا نظرة عامة على النشر (الفيديو باللغة الإنجليزية).
ما اختبرناه فعليًا: لوحة معلومات حقيقية أنشأها Claude Code في الأصل، نشرها مشغّلنا على VPS بنظام Linux مخصّص للتجارب. تحققنا من DNS العام، وHTTPS الموثوق، وإعادة التوجيه من HTTP إلى HTTPS، وتشغيل عمليتي التطبيق والوكيل دون صلاحيات root، وإمكانية الوصول بعد إعادة تشغيل خدمة التطبيق. لم يُجرَ أي نشر ذاتي جديد بواسطة Claude. والموجّه (prompt) المقترح أدناه تعليمي وليس ردًا مسجَّلًا.
قبل أن تبدؤوا
- VPS بنظام Linux مع systemd وحساب SSH بصلاحيات sudo. استخدموا VPS تجريبيًا أولًا، ولا تستبدلوا خدمات موقع قائم أو إعدادات الويب الخاصة به.
- بيئة تشغيل Node.js LTS مدعومة مثبَّتة على الخادم، وCaddy مثبَّت كخدمة نظام. ثبّتوا هذه المتطلبات قبل اتباع خطوات النشر.
- نطاق أو نطاق فرعي يمكنكم تعديل سجلات DNS الخاصة به.
- تطبيق صغير مكتفٍ بذاته. مثالنا يقدّم
index.htmlباستخدامserver.mjs، دون أي تبعيات حزم أو قاعدة بيانات.
تحتاج تطبيقات الأطر (frameworks) إلى أمر بناء وأمر تشغيل وتبعيات وخطة تخزين خاصة بها. لا تفترضوا أن مشروع Next.js أو Python أو مشروعًا يعتمد على قاعدة بيانات يمكنه استخدام هذا المثال المكوَّن من ملفين دون تعديل.
1. اطلبوا من Claude Code فحص المشروع قبل تغييره
ابدؤوا من مجلد مشروعكم المحلي. طلب أولي مفيد هو:
Inspect this project without changing files. Identify its runtime,
build and start commands, configuration and secrets. Propose a Linux
VPS deployment using a non-root systemd service and Caddy HTTPS.
Bind the app to 127.0.0.1:3000. Explain each proposed file and
verification step. Do not connect to a server or edit DNS.
تتضمن سير العمل الموثّقة لـ Claude Code التخطيط قبل التعديل. راجعوا التغييرات المقترحة بدلًا من قبول أوامر النشر دون تمحيص. أبقوا مفاتيح API وبيانات اعتماد الإنتاج ومفاتيح SSH الخاصة بعيدًا عن الموجّهات والمستودعات؛ فالوصول للقراءة فقط لا يجعل الملفات الحساسة سرّية.
2. جهّزوا التطبيق وانقلوه
في هذا المثال المصغّر تحديدًا، احفظوا ملف HTML الخاص بكم باسم index.html واستخدموا الخادم التالي. فهو يرتبط بواجهة loopback افتراضيًا ولا يكشف ملفات المشروع الأخرى:
import {createServer} from 'node:http';
import {readFileSync} from 'node:fs';
import {fileURLToPath} from 'node:url';
const page=readFileSync(new URL('./index.html',import.meta.url));
export function createAppServer(){
return createServer((req,res)=>{
const pathname=new URL(req.url,'http://localhost').pathname;
if(pathname!=='/' && pathname!=='/index.html'){
res.writeHead(404,{'Content-Type':'text/plain; charset=utf-8'});res.end('Not found');return;
}
res.writeHead(200,{'Content-Type':'text/html; charset=utf-8','X-Content-Type-Options':'nosniff'});res.end(page);
});
}
if(process.argv[1]===fileURLToPath(import.meta.url)){
const host=process.env.HOST||'127.0.0.1';const port=Number(process.env.PORT||3000);
const server=createAppServer();server.listen(port,host,()=>console.log(`Demo app listening on ${host}:${port}`));
process.on('SIGTERM',()=>server.close(()=>process.exit(0)));
}
شغّلوه محليًا بالأمر node server.mjs، وافتحوا http://127.0.0.1:3000، ثم أوقفوه قبل المتابعة. وإذا كان المنفذ 3000 مستخدمًا على VPS لديكم، فاختاروا منفذًا خاصًا آخر وطبّقوه بشكل متسق في الخدمة والوكيل معًا.
من مجلد مشروعكم المحلي، استبدلوا deploy وVPS_IP بحساب SSH الخاص بكم وعنوان الخادم الفعلي:
ssh deploy@VPS_IP 'mkdir -p ~/my-app'
scp index.html server.mjs deploy@VPS_IP:~/my-app/
على الخادم، ثبّتوا هذين الملفين غير السريين في مجلد تطبيق قابل للقراءة:
sudo install -d -m 0755 /opt/my-app
sudo install -m 0644 ~/my-app/index.html ~/my-app/server.mjs /opt/my-app/
command -v node
دوّنوا المسار المطلق لملف Node التنفيذي. يجب أن يكون متاحًا خارج صدفة تسجيل الدخول والمجلد الرئيسي (home) لديكم. التثبيت الخاص بالمستخدم عبر مدير إصدارات تحت /home لا يناسب الخدمة المحصَّنة أدناه. لا تنسخوا أبدًا ملف .env للتطوير أو مفتاحًا خاصًا أو نسخة من قاعدة بيانات إلى مجلد ويب عام.
3. شغّلوا التطبيق كخدمة systemd بلا صلاحيات root
استخدموا sudoedit /etc/systemd/system/my-app.service. استبدلوا /ABSOLUTE/PATH/TO/node بالمسار الفعلي لملف التنفيذ على مستوى الخادم الذي تحققتم منه للتو:
[Unit]
Description=My Node web app
After=network.target
[Service]
Type=simple
DynamicUser=true
WorkingDirectory=/opt/my-app
ExecStart=/ABSOLUTE/PATH/TO/node /opt/my-app/server.mjs
Environment=NODE_ENV=production
Environment=HOST=127.0.0.1
Environment=PORT=3000
Restart=on-failure
RestartSec=2
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
[Install]
WantedBy=multi-user.target
توثّق ضوابط التنفيذ في systemd الحساب الديناميكي وإعدادات التحصين. يناسب هذا الملف الشخصي تطبيقنا للقراءة فقط؛ أما التطبيقات التي تكتب ملفات مرفوعة أو قواعد بيانات فتتطلب تخزينًا دائمًا وأذونات مضبوطة عن قصد.
sudo systemctl daemon-reload
sudo systemctl enable --now my-app.service
sudo systemctl status my-app.service --no-pager
curl -fsS http://127.0.0.1:3000/
عالجوا أخطاء التطبيق قبل لمس DNS. الخدمة المفعَّلة مضبوطة للبدء عند الإقلاع، لكن هذا ليس دليلًا على اكتمال اختبار إعادة التشغيل.
4. وجّهوا نطاقًا فرعيًا إلى VPS
أنشئوا سجل A لاسم المضيف الذي اخترتموه، مثل app.example.com، يشير إلى عنوان IPv4 العام لـ VPS لديكم. مع Cloudflare، استخدم عرضنا التوضيحي للخادم المباشر وضع DNS only وقيمة TTL تلقائية. لا تنسخوا عنوان IP المستخدم في عرضنا إلى سجلات موقعكم.
تحققوا من الإجابة العامة بالأمر dig +short A app.example.com. افحصوا أيضًا dig +short AAAA app.example.com: إذا وُجد سجل IPv6، فيجب أن يصل إلى نفس عملية النشر العاملة عبر IPv6. يكتفي DNS بربط عنوان؛ فهو لا يشغّل تطبيقكم ولا يضبط HTTPS.
5. ضعوا Caddy أمام التطبيق الخاص
أبقوا خدمة Node على 127.0.0.1:3000. اجعلوا منفذي الويب العامين 80 و443 متاحين عبر جدار حماية المزوّد وجدار حماية المضيف، دون إزالة وصول SSH الخاص بكم. لا تكشفوا المنفذ 3000 للإنترنت.
أضيفوا كتلة الموقع هذه إلى إعدادات خدمة Caddy المثبَّتة لديكم، مع استبدال اسم المضيف المثالي. احتفظوا بكتل المواقع الأخرى كما هي:
app.example.com {
encode zstd gzip
reverse_proxy 127.0.0.1:3000
}
توجّه التوجيهة reverse_proxy الطلبات العامة إلى التطبيق الخاص. وعند توفر اسم مضيف عام مؤهَّل وشروط عمل التحقق من الشهادة، يحصل HTTPS التلقائي في Caddy على الشهادات ويجدّدها ويعيد توجيه HTTP إلى HTTPS.
بالنسبة إلى خدمة النظام القياسية لـ Caddy ومسار الإعدادات القياسي:
sudo caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile
sudo systemctl reload caddy
استخدم VPS التصوير لدينا خدمة Caddy مخصّصة ومسارات إعدادات معزولة بدلًا من الخدمة المشتركة القياسية أعلاه. المبدأ في التوجيه واحد؛ وهذه المسارات وأسماء الخدمات الموجَّهة للقرّاء هي تكييفات، وليست نسخة حرفية من سجل تثبيت.
6. تحققوا من النتيجة العامة ومن إعادة تشغيل الخدمة
curl -I http://app.example.com/
curl -I https://app.example.com/
sudo systemctl restart my-app.service
curl --retry 5 --retry-delay 1 --retry-connrefused -I https://app.example.com/
تحققوا من وجود إعادة توجيه HTTP إلى HTTPS، واستجابة HTTPS ناجحة، وشهادة صالحة في المتصفح. لا تستخدموا curl -k لإخفاء أخطاء الثقة. افتحوا الصفحة من شبكة أخرى، وليس من داخل VPS فقط.
يتطلب فشل فحص إعادة التشغيل تحقيقًا: افحصوا journalctl -u my-app.service -n 50 --no-pager وسجلات خدمة Caddy. أخطاء DNS، وIPv6 غير الصحيح، وتوقف التطبيق، وخطأ منفذ الخادم الخلفي، وحجب التحقق من الشهادة هي مشكلات مختلفة.
تحققنا من إعادة تشغيل خدمة التطبيق، لا من إعادة تشغيل VPS ولا من إعداد عالي التوفر. وقبل استضافة تطبيق أعمال حقيقي، خطّطوا أيضًا للتحديثات والنسخ الاحتياطية والمراقبة وإدارة الأسرار وأي بيانات دائمة.
من المشروع المحلي إلى عنوان عام
المسار الكامل واضح: جهّزوا المشروع، وانقلوه إلى VPS، وأبقوه قيد التشغيل كخدمة مُدارة بلا صلاحيات root، ووجّهوا DNS إلى الخادم، وأنهوا اتصال HTTPS العام عند الوكيل العكسي. يمكن لـ Claude Code مساعدتكم في إعداد هذه الأجزاء وشرحها؛ لكن المراجعة والفحوصات التشغيلية تظل مهمة.
استكشفوا خطط VPS Linux من SharpHeberg عندما تكونون مستعدين لنشر مشروعكم المحلي على الإنترنت. توفّر الاستضافة بيئة الخادم، وليس اشتراكًا في Claude ولا نموذج Claude مستضافًا محليًا.