SSD Nodes Learn 8GB RAM — $66/سنة
الأدلة Matt Connorبقلم Matt Connor · آخر تحديث في 2026-08-01

كيف تستضيف خادم Tailscale بنفسك باستخدام headscale

شغّل خادم التحكم الخاص بـ Tailscale على VPS: ثبّت headscale من ملف .deb الرسمي، واضبط server_url قبل تشغيله، ثم أضف أول عقدة إلى شبكتك.

Verified Every command ran end-to-end on a fresh Ubuntu 24.04 server, July 30, 2026.

ماهية headscale

headscale هو تطبيق مستضاف ذاتيًا لخادم التحكم في Tailscale، ولذلك يكون الجهاز الذي ينسّق شبكتك الخاصة عبارة عن VPS تملكه. وهو مشروع مجتمعي ولا تديره شركة Tailscale Inc. ويستمر كل جهاز في تشغيل عميل tailscale الرسمي، مع توجيهه إلى خادمك باستخدام الخيار --login-server.

خادم التحكم هو الجزء الذي يعرف الأجهزة التي تنتمي إلى الشبكة. ويمنح كل عقدة عنوانًا من 100.64.0.0/10، ويوزّع المفاتيح العامة، ويخبر العقد بمكان العثور على بعضها بعضًا. وتبقى الأنفاق باستخدام WireGuard، وتُنشأ من عقدة إلى عقدة. ولا تمر حركة الشبكة بين جهازين تملكهما عبر خادم headscale، إلا إذا تعذّر إنشاء مسار مباشر واضطرت العقد إلى استخدام مرحّل.

يخدم headscale شبكة tailnet واحدة، أي شبكة Tailscale واحدة، لكل مثيل. ويصف المشروع ذلك بأنه مناسب للاستخدام الشخصي أو لمؤسسة صغيرة. عند استخدام ثلاثة أو أربعة أجهزة، يكون استخدام شبكة VPN عادية عبر WireGuard على VPS تملكه أقل من حيث البرامج التي تحتاج إلى تشغيلها والأعطال التي قد تتعامل معها. يصبح headscale مفيدًا عندما لا تعود تريد كتابة كتلة [Peer] يدويًا لكل حاسوب محمول جديد. ولمقارنة النموذجين بمزيد من التفصيل، راجع الاختلاف بين WireGuard وTailscale.

ما تحتاج إليه قبل التثبيت

  • VPS يعمل بنظام Ubuntu 24.04، وله عنوان IPv4 عام وإمكانية الوصول باستخدام sudo. إذا كان الخادم جديدًا، فنفّذ أولًا الخطوات الواردة في الدقائق العشر الأولى على VPS جديد.
  • سجل DNS من النوع A يشير إلى ذلك العنوان. يستخدم هذا الدليل headscale.example.com.
  • نطاق أو نطاق فرعي ثانٍ لاستخدام MagicDNS. يستخدم هذا الدليل tailnet.example.net. يجب ألا يكون النطاق نفسه المستخدم في server_url.
  • جهاز عميل واحد للانضمام، يعمل بنظام Linux أو macOS أو Windows أو Android أو iOS.

تثبيت headscale من حزمة ‎.deb الرسمية

ينشر المشروع حزم .deb في صفحة الإصدارات على GitHub. اعتبارًا من يوليو 2026، الإصدار الحالي هو 0.29.3. تحقّق من البنية أولًا، لأن اسم الملف يتضمنها.

sudo apt update
sudo apt install -y wget
dpkg --print-architecture

يعرض ذلك amd64 على VPS عادي بمعمارية x86، ويعرض arm64 على خطة بمعمارية Ampere أو Graviton. ضع النتيجة في المتغير أدناه.

HEADSCALE_VERSION="0.29.3"
HEADSCALE_ARCH="amd64"
wget --output-document=headscale.deb \\
  "https://github.com/juanfont/headscale/releases/download/v${HEADSCALE_VERSION}/headscale_${HEADSCALE_VERSION}_linux_${HEADSCALE_ARCH}.deb"
sudo apt install -y ./headscale.deb
headscale version

بادئة ./ أمام اسم الملف مطلوبة. بدونها، يبحث apt عن حزمة اسمها headscale.deb في مستودعاتك ويفشل.

تنشئ الحزمة مستخدم نظام headscale، وتكتب ملف إعداد افتراضيًا /etc/headscale/config.yaml، وتثبّت وحدة systemd. لكنها لا تبدأ الخدمة، وهذا هو الترتيب الصحيح. يوجّه الإعداد المضمّن server_url إلى http://127.0.0.1:8080، وهو ليس عنوانًا يمكن لأي عميل لديك الوصول إليه. لذلك ستكون الخدمة التي تبدأ الآن مهيّأة بشكل خاطئ حتى لو بدأت بنجاح. يؤدي تشغيل sudo systemctl is-active headscale في هذه المرحلة إلى عرض inactive. هذا متوقع وليس عطلًا.

اضبط server_url قبل بدء تشغيل الخدمة

حرّر /etc/headscale/config.yaml باستخدام sudo nano /etc/headscale/config.yaml، أو طبّق التغييرات الثلاثة نفسها باستخدام sed. احتفظ بنسخة من الملف الأصلي، لأن الملف طويل ويحتوي على تعليقات كثيرة، وهو أفضل مرجع متاح لك لبقية الإعدادات.

sudo cp /etc/headscale/config.yaml /etc/headscale/config.yaml.orig
sudo sed -i 's|^server_url:.*|server_url: https://headscale.example.com|' /etc/headscale/config.yaml
sudo sed -i 's|^listen_addr:.*|listen_addr: 127.0.0.1:8080|' /etc/headscale/config.yaml
sudo sed -i 's|^  base_domain:.*|  base_domain: tailnet.example.net|' /etc/headscale/config.yaml
sudo grep -E '^(server_url|listen_addr):|^  base_domain:' /etc/headscale/config.yaml

server_url هو العنوان الذي يكتبه headscale في كل تسجيل للعميل. سيتصل العملاء بهذه السلسلة النصية نفسها بشكل دائم بعد ذلك، لذلك يجب أن تكون الاسم العام مع https:// في المقدمة، وليس 127.0.0.1 مطلقًا.

listen_addr هو العنوان الذي تستمع عليه العملية. اتركه على loopback. ينهي reverse proxy الموجود على الخادم نفسه TLS (أمان طبقة النقل) ويمرر الطلبات إليه، لذلك لا يحتاج أي شيء خارج الخادم إلى الوصول إلى المنفذ 8080.

base_domain هي لاحقة MagicDNS، أي النطاق الذي تحصل العقد ضمنه على أسمائها. يجب أن تكون اسم نطاق مؤهلًا بالكامل من دون نقطة في نهايته، وأن تكون نطاقًا مختلفًا عن النطاق الموجود في server_url، وإلا ستتعارض مساحتا الأسماء.

اترك قسم قاعدة البيانات كما هو. الإعداد الافتراضي هو SQLite في /var/lib/headscale/db.sqlite، داخل دليل أنشأته الحزمة وتملكه، وSQLite كافية لشبكة tailnet بهذا الحجم.

شغّل headscale وتحقّق من أنه يعمل

sudo systemctl enable --now headscale
sudo systemctl is-active headscale
curl -sS -o /dev/null -w '%{http_code}\\n' http://127.0.0.1:8080/health

يعرض is-active القيمة active، ويعرض curl القيمة 200. ينفّذ enable --now جزأي المهمة: يشغّل الخدمة ويضبطها لكي تبدأ بعد إعادة التشغيل.

إذا عرض is-active القيمة failed، فاقرأ السجل باستخدام sudo journalctl -u headscale -n 50 --no-pager. يكون سبب الفشل في هذه المرحلة هو ملف الإعدادات في كل الحالات تقريبًا، لأن headscale يحلّل الملف بالكامل قبل أن يفتح socket. لذلك يؤدي وجود مسافة بادئة غير صحيحة أو مفتاح غير معروف إلى إيقاف العملية قبل أن تبدأ الاستماع. أصلح الملف، ثم نفّذ sudo systemctl restart headscale. يتطلب كل تغيير لاحق في الإعدادات إعادة التشغيل بالطريقة نفسها. يعيد العملاء الاتصال تلقائيًا بعد ذلك. إذا لم تكن وحدات systemd مألوفة لك، يشرح تشغيل خدماتك ومؤقتاتك باستخدام systemd الأوامر المستخدمة هنا.

تحقّق من ملفات الحالة أثناء بقائك في shell:

stat -c '%U %n' /var/lib/headscale/db.sqlite /var/lib/headscale/noise_private.key

يبدأ السطران بـ headscale، وهو المستخدم غير المميّز الذي أنشأته الحزمة. يمثّل noise_private.key هوية الخادم لدى عملائه. احتفظ به. إذا حذفته، ينشئ headscale هوية جديدة، ويتعين على كل node التسجيل مرة أخرى.

ضع TLS أمام headscale

يجب أن يصل العملاء إلى server_url عبر HTTPS. يُعد Caddy أقصر طريق، لأنه يطلب الشهادة ويجددها تلقائيًا.

sudo apt install -y caddy

استبدل /etc/caddy/Caddyfile بالكتلة الواردة في وثائق headscale:

headscale.example.com {
    reverse_proxy 127.0.0.1:8080 {
        header_up True-Client-IP {remote_host}
        header_up X-Real-IP {remote_host}
    }
}
sudo caddy validate --adapter caddyfile --config /etc/caddy/Caddyfile
sudo systemctl restart caddy
sudo systemctl is-active caddy

يعرض validate القيمة adapted config to JSON عند تحليل الملف بنجاح. والتحذير من أن الملف غير منسق ليس مهمًا. ومن حاسوبك المحمول، يجب أن يعرض curl -sS -o /dev/null -w '%{http_code}\\n' https://headscale.example.com/health القيمة 200 أيضًا. يثبت هذا الفحص الواحد أن DNS وجدار الحماية والشهادة والوكيل تعمل معًا.

إليك تفصيل الوكيل الذي قد يستهلك منك مساءً كاملًا. اتصال التحكم في Tailscale هو ترقية HTTP، ويبدأ باستخدام POST بدلًا من GET، وتكون قيمة ترويسة Upgrade هي tailscale-control-protocol. يمرر Caddy ذلك دون إعداد إضافي. أما nginx فلا يفعل ذلك، لذلك تحتاج واجهة nginx الأمامية إلى خريطة الترقية:

map $http_upgrade $connection_upgrade {
    default keep-alive;
    ''      close;
}

server {
    listen 443 ssl;
    server_name headscale.example.com;
    location / {
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_buffering off;
        proxy_pass http://127.0.0.1:8080;
    }
}

إذا حذفت هذه الأسطر، فستستمر الطلبات العادية في النجاح. لذلك يعرض /health القيمة 200 ويبدو كل شيء سليمًا، بينما لا يتشكل اتصال التحكم طويل الأمد مطلقًا، وتسجل عُقدك نفسها ثم تبقى غير متصلة. إذا اخترت استخدام nginx، يشرح Certbot على Ubuntu 24.04 مع nginx جانب الشهادة.

المنافذ التي يجب فتحها في UFW

sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbose

ينقل المنفذ 443 جميع اتصالات العملاء. يُستخدم المنفذ 80 فقط لتحدي HTTP الخاص بـ ACME (بيئة إدارة الشهادات تلقائيًا) ولإعادة التوجيه إلى HTTPS، ويحتاج Caddy إليه للحصول على شهادة أصلًا.

يبقى المنفذ 8080 مغلقًا. listen_addr هو 127.0.0.1:8080، لذلك يصل الوكيل إلى headscale عبر واجهة loopback، ولا تكون هناك حاجة إلى قاعدة جدار ناري. فتح المنفذ 8080 أمام الإنترنت يتيح للعملاء قناة تحكم بنص واضح ولا يحقق أي فائدة. تذكّر أن معظم موفري الخدمة يشغّلون جدارًا ناريًا ثانيًا في لوحة التحكم، منفصلًا عن UFW، لذلك قد يكون المنفذ مفتوحًا على الخادم لكنه مغلقًا عند الحافة. يشرح أساسيات جدار UFW الناري على VPS صياغة القواعد بمزيد من التفصيل.

إنشاء مستخدم ومفتاح مصادقة مسبقة

sudo headscale users create alice
sudo headscale users list

الأمر headscale هو عميل. ويتصل بالعفريت قيد التشغيل عبر مقبس Unix الموجود في /var/run/headscale/headscale.sock، والذي وضعه 0770 ومالكه مجموعة headscale. ويترتب على ذلك أمران. يفشل الأمر عندما تكون الخدمة متوقفة، وهذا سبب آخر لأهمية ترتيب الخطوات في هذا الدليل، كما يتطلب sudo ما لم تضف حسابك إلى مجموعة headscale.

يطبع users list معرّفًا بجانب كل اسم. تحتاج إلى هذا الرقم، لأن أمر المفتاح يتطلب معرّف مستخدم رقميًا وليس اسمًا.

sudo headscale preauthkeys create --user 1 --expiration 24h

يُطبع المفتاح مرة واحدة فقط. انسخه الآن. مفتاح المصادقة المسبقة صالح لاستخدام واحد ولمدة ساعة واحدة، ما لم تحدد خلاف ذلك، لذلك من المفيد ضبط --expiration 24h أثناء الاختبار. أضف --reusable لإنشاء مفتاح يتيح تسجيل عدة أجهزة، وتعامل معه مثل كلمة مرور، لأن أي شخص يملكه يمكنه الانضمام إلى شبكتك.

وصّل أول عميل باستخدام --login-server

على الجهاز الذي تريد ضمّه:

curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up --login-server https://headscale.example.com --auth-key 'hskey-auth-PASTE-YOUR-KEY-HERE'
tailscale status
tailscale ip -4

يطبع tailscale ip -4 العنوان الذي عيّنه headscale، مثل 100.64.0.1. ارجع إلى الخادم، حيث يعرض sudo headscale nodes list العقدة مع معرّفها ومستخدمها وحالتها المتصلة.

يجب أن تتطابق قيمة --login-server مع server_url تمامًا، بما في ذلك المخطط، ومن دون شرطة مائلة في النهاية. تجري مقارنتهما كسلاسل نصية، ويؤدي عدم التطابق إلى تسجيل العميل في عنوان ثم توجيهه للتحدث إلى عنوان آخر.

يحتفظ الجهاز الذي سجّل الدخول سابقًا إلى خدمة Tailscale المستضافة بعملية تسجيل الدخول تلك. شغّل sudo tailscale logout عليه أولًا، ثم شغّل tailscale up باستخدام --login-server.

إذا حذفت --auth-key، يطبع العميل عنوان URL بدلًا من ذلك. افتحه، وستعرض الصفحة المعرّف الخاص بمحاولة التسجيل تلك، ثم وافق عليها على الخادم:

sudo headscale auth register --user alice --auth-id PASTE-THE-ID-FROM-THE-PAGE

هذا النموذج أسهل على حاسوبك المحمول. أما مفاتيح ما قبل المصادقة فهي أفضل لأي عملية مُبرمجة، لأنها لا تتطلب وجود شخص للمراقبة.

DERP، وما ينقل حركة الشبكة عند فشل المسار المباشر

DERP ‏(مرحل مشفّر مخصّص للحزم) هو مسار الرجوع. عندما يتعذر على عقدتين فتح اتصال WireGuard مباشر، ويحدث ذلك عادةً لأن كلتيهما خلف NAT صارم (ترجمة عناوين الشبكة)، فإنهما ترسلان الحزم عبر مرحل بدلاً من ذلك. لا يحتفظ المرحل بأي مفاتيح، لذلك لا يمكنه قراءة حركة الشبكة لديك. لكنه يعرف أي عقد تتواصل مع بعضها وحجم البيانات المنقولة.

يجب أن تكون واضحاً بشأن ما يفعله الإعداد الافتراضي. يُشحن Headscale مع توجيه إلى https://controlplane.tailscale.com/derpmap/default، ومع auto_update_enabled: true وupdate_frequency: 3h، ولذلك تكون طبقة التحكم ملكك بينما تكون مرحلاتك تابعة لـ Tailscale. بالنسبة إلى معظم المستخدمين، هذه مقايضة مناسبة. إذا لم تكن مناسبة لك، فشغّل مرحلك الخاص.

لتشغيل مرحلك الخاص، اضبط enabled: true ضمن derp.server في config.yaml، ثم أعد تشغيل headscale، وافتح منفذ STUN (أدوات عبور الجلسات عبر NAT) باستخدام sudo ufw allow 3478/udp. يوضح ملف الإعداد المتطلب بوضوح: يجب أن يستخدم server_url البروتوكول https، لأن DERP يتطلب TLS. يؤدي إفراغ قائمة derp.urls إلى إزالة مرحلات Tailscale من الخريطة. وإذا فعلت ذلك من دون مرحل مضمّن يعمل، فلن تتمكن أي زوج من العقد التي يتعذر عليها الاتصال مباشرة من الاتصال إطلاقاً.

من جهة العميل، يطبع tailscale netcheck زمن الاستجابة لكل منطقة مرحل معروفة لديه، ويضع tailscale status علامة على كل نظير باعتباره إما direct مع عنوان، أو relay مع رمز منطقة. النظير العالق في relay يعاني من مشكلة NAT، وليس من مشكلة في headscale.

لماذا تظهر العقدة على أنها غير متصلة؟

الوكيل يسقط طلب الترقية. هذه هي الحالة الأكثر شيوعًا. علامتها أن كل شيء آخر يبدو سليمًا: يعرض /health الرمز 200، ويعرض headscale nodes list العقدة، لكن العقدة لا تتصل مطلقًا. اتصال التحكم هو طلب POST يحمل Upgrade: tailscale-control-protocol. والوكيل الذي لا يمرره يقطع القناة الوحيدة التي تبلغ عن حالة العقدة. قارن إعداد nginx لديك مع كتلة map أعلاه، أو بدّل إلى Caddy لاستبعاد الوكيل.

تغيّرت قيمة server_url بعد تسجيل العقد. تستمر العقد في الاتصال بالقيمة التي أُعطيت لها عند التسجيل. إذا عدّلتها، شغّل sudo tailscale up --login-server https://headscale.example.com --force-reauth على كل عقدة.

العميل لا يعمل. على العقدة، شغّل sudo systemctl is-active tailscaled وsudo journalctl -u tailscaled -n 50 --no-pager. يسجّل العميل الذي لا يستطيع تحليل نطاقك أو الوصول إليه محاولات إعادة الاتصال هناك.

انتهت صلاحية المفتاح. يوضّح القسم التالي هذه الحالة.

لمراقبة جانب الخادم أثناء الاختبار، شغّل sudo journalctl -u headscale -f على VPS وأعد تشغيل tailscaled على العميل. تُنتج العقدة التي تصل إلى headscale أسطر سجل فورًا. ويعني الصمت أن الطلب لا يصل. لذلك افحص DNS وجدار الحماية والوكيل قبل فحص headscale.

انتهاء صلاحية المفاتيح، والعقدة التي تتوقف عن العمل بعد أسابيع

يوجد نوعان منفصلان من انتهاء الصلاحية، والخلط بينهما يضيّع الوقت.

تنتهي صلاحية مفاتيح المصادقة المسبقة بسرعة حسب التصميم. الإعداد الافتراضي هو ساعة واحدة واستخدام واحد. إذا رفض tailscale up المفتاح، فأنشئ مفتاحًا جديدًا على الخادم بدلًا من تعديل أي شيء على العميل.

مفاتيح العقد هي الجزء طويل الأمد. يحدد قسم node في config.yaml قيمة expiry: 0، وتعني 0 عدم وجود انتهاء صلاحية افتراضي: تظل العقدة المسجلة صالحة حتى تنهي صلاحيتها. أما العقد الموسومة فلا تنتهي صلاحيتها مطلقًا. عيّن expiry: 180d إذا أردت أن تنتهي صلاحية التسجيلات تلقائيًا، وافهم ما يترتب على ذلك: ستحتاج كل عقدة غير موسومة إلى sudo tailscale up --login-server https://headscale.example.com --force-reauth وفقًا لهذا الجدول، وسينقطع خادم لا تتم إعادة مصادقته من الشبكة تلقائيًا.

نفّذ ذلك يدويًا عندما يفقد شخص ما حاسوبًا محمولًا. يعرض لك sudo headscale nodes list المعرّف، ثم يسجّل sudo headscale nodes expire -i 3 خروج تلك العقدة، ويزيلها sudo headscale nodes delete -i 3 من الشبكة بالكامل.

النسخ الاحتياطية والترقيات

يشكّل /var/lib/headscale و/etc/headscale معًا الخادم بأكمله. أوقف الخدمة قبل نسخهما، لأن SQLite قد ينفّذ عمليات كتابة جارية، وقد تكون قاعدة البيانات المنسوخة أثناء التشغيل غير متسقة.

sudo systemctl stop headscale
sudo tar czf /root/headscale-state.tgz -C /var/lib headscale
sudo tar czf /root/headscale-config.tgz -C /etc headscale
sudo systemctl start headscale
sudo chmod 600 /root/headscale-*.tgz

انقل الملفين خارج الخادم. فهما يحتويان على المفاتيح الخاصة وكل عمليات التسجيل، ولذلك يجب التعامل معهما بالعناية نفسها المطبقة على الخادم. يشرح النسخ الاحتياطية باستخدام restic من VPS كيفية تنفيذ ذلك وفق جدول زمني وبطريقة مشفّرة.

تتبع الترقيات خطوات التثبيت نفسها: نزّل .deb وsudo apt install ./headscale.deb الجديدين، ثم أعد التشغيل وأعد تنفيذ اختبارات is-active و/health. منذ الإصدار 0.29، أصبح مسار الترقية صارمًا. لا يُسمح بتجاوز إصدار فرعي، كما لا يُسمح بالرجوع إلى إصدار فرعي أقدم. انتقل إصدارًا فرعيًا واحدًا في كل مرة، وخذ نسخة احتياطية قبل كل خطوة، واقرأ ملاحظات إصدار ذلك الإصدار أولًا، لأن الإصدار نفسه غيّر سلوك سياسة ACL ونقل عدة مفاتيح إعدادات.

FAQ

لماذا يفشل headscale في بدء التشغيل مباشرة بعد تثبيت ملف .deb؟

تثبّت الحزمة الوحدة، لكنها تترك الخدمة متوقفة، كما أن /etc/headscale/config.yaml الافتراضي قالب وليس إعدادًا صالحًا للعمل. حرّر server_url وlisten_addr وbase_domain أولًا، ثم شغّل sudo systemctl enable --now headscale وتحقق باستخدام sudo systemctl is-active headscale. إذا استمر الفشل، فسيحدد sudo journalctl -u headscale -n 50 --no-pager المشكلة. وفي هذه المرحلة يكون السبب غالبًا خطأ في YAML، لأن headscale يحلل الملف بالكامل قبل أن يبدأ الاستماع على منفذ.

هل ما زلت أحتاج إلى تثبيت عميل Tailscale العادي على أجهزتي؟

نعم. يستبدل Headscale خادم التحكم فقط. ويشغّل كل عقدة العميل الرسمي من Tailscale، ثم توجّهه إلى خادمك باستخدام sudo tailscale up --login-server https://headscale.example.com. هذا الخيار موجود في العميل القياسي، لذلك لا تحتاج إلى تصحيح أي شيء أو إعادة بناء العميل.

هل تمر حركة الشبكة عبر خادم headscale؟

عادةً لا. ينسّق Headscale الشبكة ويوزّع المفاتيح والعناوين، بينما يكون مسار البيانات عبر WireGuard مباشرةً بين عقدك. لا تسلك حركة الشبكة مسارًا وسيطًا إلا عندما يتعذر على عقدتين الوصول إلى بعضهما مباشرةً، فتنتقلان إلى مرحّل DERP. ومع الإعداد الذي يأتي مع الحزمة، تكون هذه المرحّلات هي المرحّلات العامة التابعة لـ Tailscale. شغّل tailscale status على إحدى العقد لمعرفة ما إذا كان نظير معيّن في حالة direct أو يستخدم relay.

لماذا تظل عقدتي غير متصلة بالإنترنت بعد تسجيلها؟

العقدة التي تظهر في headscale nodes list لكنها لا تتصل مطلقًا تكون قد فقدت عادةً اتصال التحكم عند الوكيل العكسي. هذا الاتصال هو ترقية HTTP تُرسل باستخدام POST مع الترويسة Upgrade: tailscale-control-protocol، ويسقطها nginx ما لم تضف كتلة map $http_upgrade $connection_upgrade وأسطر proxy_set_header المطابقة. يمرّر Caddy هذا الاتصال دون إعداد إضافي، لذلك يمكنك استخدامه سريعًا لاختبار ما إذا كان الوكيل هو سبب المشكلة.

هل أحتاج إلى اسم نطاق وTLS من أجل headscale؟

عمليًا، نعم. يتصل العملاء بأي سلسلة تضعها في server_url، وتُصدر الشهادات للأسماء لا لعناوين IP المجردة، كما يوضح ملف الإعداد أن DERP يتطلب TLS. يستغرق إعداد نطاق مع Caddy نحو خمس دقائق، ويمنحك نقطة نهاية HTTPS تجدّد الشهادة تلقائيًا. ويعني تشغيل خادم التحكم عبر HTTP العادي أن كل اتصال بين العملاء والخادم يمر عبر الإنترنت دون تشفير.