التسويق والمبيعات الأدلة التقنية

منظومة تسويق كاملة على سيرفرك: من الرابط إلى العميل

نجمع Umami وShlink وMautic في ملف Compose واحد، ونتتبع عميلاً من رابط مختصر في منشور حتى يظهر تحويله ومصدره في التقارير.

منظومة تسويق كاملة على سيرفرك: من الرابط إلى العميل

كل أداة في مقالات هذا المسار جربناها وحدها: الرابط المختصر يعد النقرات، وأداة التحليلات تعد الزيارات، وأداة الأتمتة ترسل الرسائل. ولكن السؤال الذي يسأله مدير الشركة لا تجيب عنه أداة واحدة: «العميل الذي فعل حسابه اليوم، من أين جاء؟». ولكي تجيب عنه يجب أن تمر معلومة المصدر من أداة إلى أخرى دون أن تضيع. وفي هذا الدليل سوف نجمع الأدوات في ملف Compose واحد على سيرفرنا، ثم نمر بمسار عميل كامل: رابط مختصر في منشور، ثم صفحة هبوط، ثم نموذج، ثم جهة اتصال في Mautic، ثم رسالة تفعيل، ثم تحويل Conversion يظهر في التقارير مع مصدره.

ولن نعيد شرح ما سبق، فمعنى معاملات UTM والتحويل ونماذج الإسناد Attribution في المقال الأول من القائمة التالية، وكل أداة لها مقالها:

دليلقياس التسويق: روابط UTM والإسناد Attributionكيف تعرف القناة التي جاءت بكل عميل: روابط UTM بقواعد تسمية ثابتة، وتحويل تعرفه أنت، ونموذج إسناد تفهم ما يحسبه.دليلالروابط المختصرة وتتبع النقرات: Bitly وبدائلهBitly وDub أمام Shlink وYOURLS على نطاقك، مع تجربة Shlink في Docker تبين لماذا لا يساوي عدد النقرات عدد الزوار.دليلتحليلات الموقع دون Google Analytics: مقارنة عمليةشغلنا Umami وPlausible وMatomo على نفس الموقع وقارناها بـ Google Analytics في حجم السكربت والـ Cookies والعربية وملكية البيانات.دليلMautic على سيرفرك: حملة مؤتمتة من النموذج إلى البريدنشغل Mautic في Docker ونبني نموذجاً عربياً ونقاطاً للتقييم وحملة ترسل الترحيب وتنبه المبيعات، ونختبرها بزائرين تجريبيين.

وفي هذا الدليل سوف نقوم بما يلي:

  • نشغل Umami وShlink وMautic وMailpit وصفحة هبوط في ملف Compose واحد بإصدارات مثبتة.
  • ننشئ رابطين مختصرين لحملة واحدة، أحدهما لمنشور في X والآخر لمنشور في LinkedIn.
  • نمر بزائرين: الأول يطلب نسخة تجريبية ويفعل حسابه من الرسالة، والثاني يفتح الصفحة ويغادر.
  • نتتبع المعلومة في كل أداة بالأرقام، ونرى أين ضاعت معاملات UTM وأين وصلت.
  • نقارن الإسناد للنقرة الأولى First Click والأخيرة Last Click، ونرى لماذا يعطيان جوابين مختلفين للعميل نفسه.

والصورة التالية تبين المسار وأين تسجل كل خطوة:

سبع خطوات: منشور في X فيه الرابط المختصر localhost:18796/launch، ثم Shlink يحسب النقرة ويحول برمز 302، ثم صفحة الهبوط فيها سكربت Umami وسكربت Mautic وترسل الزيارة وحدث trial-request إلى Umami، ثم إرسال النموذج إلى Mautic الذي ينشئ جهة اتصال ونقاطاً ويرسل رسالة فورية، ثم الرسالة في Mailpit بمعاملات utm_source=mautic، ثم صفحة التفعيل activate.html، ثم حدث activate يعود إلى Umami ويجيب تقرير الإسناد عن مصدر العميل
كل أداة تسجل جزءاً من المسار، والرابط بينها معاملات UTM

المتطلبات

  • سيرفر Linux عليه Docker وDocker Compose، وقد اختبرنا على Docker Engine 29.8.0.
  • ذاكرة: بعد التجربة كانت الخدمات العشر تستهلك حوالي 2.1 جيجابايت، أكبرها قاعدة بيانات Mautic بـ 544 ميجابايت، ثم Shlink بـ 500، ثم الـ Worker الخاص بـ Mautic بـ 372، ثم Umami بـ 303، لذلك خطط لسيرفر بذاكرة 4 جيجابايت على الأقل.
  • مساحة القرص: حوالي 3.2 جيجابايت لـ Image الخاصة بـ Mautic، و1.36 لـ Umami، و1.1 لـ MySQL، و650 ميجابايت لـ PostgreSQL، و465 لـ Shlink.
  • في الإنتاج: نطاق فرعي لكل أداة بشهادة TLS، مثل stats.example.com وgo.example.com وm.example.com، خلف Reverse Proxy مثل Caddy.

ملف Compose واحد للمنظومة

الملف التالي يجمع أربع مجموعات، ولكل منها قاعدة بياناتها، ولا يوجد بينها اعتماد في التشغيل، فإذا توقفت Umami مثلاً بقيت النماذج والرسائل تعمل:

name: mke-stack

x-mautic: &mautic
  image: mautic/mautic:7.2.1-apache
  restart: unless-stopped
  env_file: .mautic_env
  volumes:
    - mautic-config:/var/www/html/config
    - mautic-logs:/var/www/html/var/logs
    - mautic-media:/var/www/html/docroot/media

services:
  # 1) web analytics
  umami-db:
    image: postgres:18.6
    restart: unless-stopped
    environment:
      POSTGRES_DB: umami
      POSTGRES_USER: umami
      POSTGRES_PASSWORD: ${UMAMI_DB_PASSWORD}
    volumes:
      - umami-db:/var/lib/postgresql
    healthcheck:
      test: pg_isready -U umami -d umami
      interval: 5s
      retries: 30
      start_period: 120s

  umami:
    image: ghcr.io/umami-software/umami:3.4.1
    restart: unless-stopped
    environment:
      DATABASE_URL: postgresql://umami:${UMAMI_DB_PASSWORD}@umami-db:5432/umami
      APP_SECRET: ${UMAMI_APP_SECRET}
    ports:
      - "127.0.0.1:18795:3000"
    depends_on:
      umami-db:
        condition: service_healthy

  # 2) short links
  shlink:
    image: shlinkio/shlink:5.1.8
    restart: unless-stopped
    environment:
      DEFAULT_DOMAIN: localhost:18796
      IS_HTTPS_ENABLED: "false"
      INITIAL_API_KEY: ${SHLINK_API_KEY}
      TIMEZONE: Asia/Riyadh
    volumes:
      - shlink-data:/etc/shlink/data
    ports:
      - "127.0.0.1:18796:8080"

  shlink-web:
    image: shlinkio/shlink-web-client:4.8.1
    ports:
      - "127.0.0.1:18797:8080"

  # 3) marketing automation
  mautic-db:
    image: mysql:8.4.11
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: mautic
      MYSQL_USER: mautic
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    volumes:
      - mysql-data:/var/lib/mysql
    healthcheck:
      test: mysqladmin --user=mautic --password=$$MYSQL_PASSWORD ping
      interval: 5s
      timeout: 5s
      retries: 60
      start_period: 300s

  mautic_web:
    <<: *mautic
    ports:
      - "127.0.0.1:18799:80"
    healthcheck:
      test: curl -fs http://localhost/ -o /dev/null || exit 1
      interval: 10s
      timeout: 5s
      retries: 60
      start_period: 300s
    depends_on:
      mautic-db:
        condition: service_healthy

  mautic_cron:
    <<: *mautic
    environment:
      DOCKER_MAUTIC_ROLE: mautic_cron
    depends_on:
      mautic_web:
        condition: service_healthy

  mautic_worker:
    <<: *mautic
    environment:
      DOCKER_MAUTIC_ROLE: mautic_worker
    depends_on:
      mautic_web:
        condition: service_healthy

  # 4) mail sink and the landing page
  mailpit:
    image: axllent/mailpit:v1.31.5
    ports:
      - "127.0.0.1:18800:8025"

  site:
    image: nginx:1.31.6-alpine
    volumes:
      - ./site:/usr/share/nginx/html:ro
    ports:
      - "127.0.0.1:18798:80"

volumes:
  umami-db:
  shlink-data:
  mysql-data:
  mautic-config:
  mautic-logs:
  mautic-media:

والملف .mautic_env هو نفسه في دليل Mautic مع تغيير اسم قاعدة البيانات إلى mautic-db. وننشئ الأسرار الخمسة ثم نشغل:

printf 'UMAMI_DB_PASSWORD=%s\nUMAMI_APP_SECRET=%s\nSHLINK_API_KEY=%s\nMYSQL_PASSWORD=%s\nMYSQL_ROOT_PASSWORD=%s\n' \
  "$(openssl rand -hex 16)" "$(openssl rand -hex 32)" "$(openssl rand -hex 16)" "$(openssl rand -hex 16)" "$(openssl rand -hex 16)" > .env
chmod 600 .env
docker compose up -d

في الإعداد أعلاه لاحظ التالي:

  • Shlink يستخدم SQLite داخل الـ Volume المسمى shlink-data لأننا لم نعطه قاعدة بيانات، وهذا يكفي لتجربة أو لعدد قليل من الروابط، أما في الإنتاج فالأفضل PostgreSQL كما في توثيق Shlink.
  • القيمة INITIAL_API_KEY تنشئ أول مفتاح API عند أول تشغيل فقط، فاحفظها في مدير كلمات المرور.
  • الخدمة shlink-web واجهة رسومية تعمل في المتصفح بالكامل، أي أن مفتاح الـ API يحفظ في متصفحك وليس على السيرفر، ويمكنك الاستغناء عنها والعمل بالـ API.

تجهيز كل أداة

شرحنا تثبيت كل أداة في مقالها، وهنا ما أضفناه لهذا المسار فقط:

  • Umami: غيرنا كلمة مرور المستخدم الافتراضي admin فوراً، ثم أنشأنا موقعاً باسم «منصة الفواتير» فأعطانا معرف الموقع Website ID.
  • Mautic: ثبتناه بالأمر mautic:install، وضبطنا البريد إلى Mailpit، والمنطقة الزمنية، وcors_valid_domains لصفحة الهبوط، ثم أعدنا تشغيل الـ Worker والـ Cron، والسبب في دليل Mautic.
  • Mautic: أنشأنا رسالة «تفعيل الحساب التجريبي» بمعاملات UTM ثابتة utm_source=mautic وutm_medium=email وutm_campaign=launch-oct، ونموذجاً بثلاثة حقول وثلاثة إجراءات: 20 نقطة، وحفظ معاملات UTM Record UTM Tags، وإرسال الرسالة فوراً Send email to contact.

واخترنا إرسال الرسالة من إجراء في النموذج وليس من حملة، لأن الحملة تنتظر الـ Cron كل 15 دقيقة، بينما رسالة التفعيل يجب أن تصل والعميل ما زال أمام الشاشة.

ثم أنشأنا الرابطين المختصرين من الـ API الخاصة بـ Shlink، ولاحظ أن معاملات UTM جزء من الرابط الطويل، أي أن من يكتب المنشور يأخذ رابطاً قصيراً لا يستطيع أن يخطئ فيه:

{"longUrl":"http://localhost:18798/?utm_source=x&utm_medium=social&utm_campaign=launch-oct&utm_content=post-1","customSlug":"launch","title":"إطلاق منصة الفواتير: منشور X","tags":["launch-oct"],"findIfExists":true}
curl -s -X POST http://localhost:18796/rest/v3/short-urls -H "X-Api-Key: $SHLINK_API_KEY" \
  -H 'Content-Type: application/json' --data-binary @short.json

والرابط الثاني مثله بالاسم launch-in والقيمة utm_source=linkedin، وكلاهما يحول برمز 302:

302 -> http://localhost:18798/?utm_source=x&utm_medium=social&utm_campaign=launch-oct&utm_content=post-1
واجهة Shlink فيها رابطان مختصران أنشئا في 2026-10-25: localhost:18796/launch-in بعنوان إطلاق منصة الفواتير منشور LinkedIn، وlocalhost:18796/launch بعنوان إطلاق منصة الفواتير منشور X، وكلاهما بالوسم launch-oct وعلى كل منهما زيارة واحدة
الرابطان المختصران في Shlink، لكل منهما زيارة واحدة بعد التجربة

صفحة الهبوط: سكربتان وحدثان

في صفحة الهبوط نضع سكربت Umami لعد الزيارات، وسكربت Mautic لربط الزيارة بجهة الاتصال، ثم النموذج، ثم سطر يرسل حدثاً إلى Umami عند إرسال النموذج بالدالة umami.track الموثقة في دوال التتبع:

<script defer src="http://localhost:18795/script.js" data-website-id="999dfc10-3ae8-45a5-b50e-e8feb3266f09"></script>
<script>
  (function(w,d,t,u,n,a,m){w["MauticTrackingObject"]=n;
    w[n]=w[n]||function(){(w[n].q=w[n].q||[]).push(arguments)},a=d.createElement(t),
    m=d.getElementsByTagName(t)[0];a.async=1;a.src=u;m.parentNode.insertBefore(a,m)
  })(window,document,"script","http://localhost:18799/mtc.js","mt");
  mt("send", "pageview");
</script>

<script src="http://localhost:18799/form/generate.js?id=1"></script>

<script>
  // conversion event in Umami when the trial form is sent
  document.addEventListener('submit', function (e) {
    if (e.target.matches('form[data-mautic-form]') && window.umami) umami.track('trial-request');
  }, true);
</script>

وفي صفحة التفعيل activate.html نفس السكربتين، ومعهما حدث activate بعد التحميل، وهو التحويل الذي نريد أن نعرف مصدره:

<script>
  window.addEventListener('load', function () { setTimeout(function () { if (window.umami) umami.track('activate'); }, 500); });
</script>

والسبب في التأخير القصير أن سكربت Umami محمل بالخاصية defer، فقد لا تكون الدالة umami جاهزة في نفس لحظة التحميل.

المسار بزائرين

الزائر الأول فتح الرابط launch-in من هاتف iPhone، فرأى الصفحة وغادر. والزائرة الثانية سارة فتحت الرابط launch من متصفح Chrome على جهاز مكتبي، وكتبت بياناتها في النموذج:

صفحة الهبوط العربية جرب منصة الفواتير 14 يوماً، وفي النموذج الاسم سارة والبريد sara@example.com والشركة متجر سارة للعطور، وزر أرسل الطلب
صفحة الهبوط وصلت إليها سارة من الرابط المختصر

ووصلت رسالة التفعيل إلى Mailpit في أقل من ثانيتين من إرسال النموذج، لأنها خرجت من إجراء النموذج مباشرة إلى طابور الـ Worker دون انتظار الـ Cron:

Mailpit يعرض رسالة من فريق منصة الفواتير إلى سارة بعنوان سارة، حسابك التجريبي جاهز للتفعيل، والمحتوى من اليمين إلى اليسار مع زر تفعيل الحساب ورابط إلغاء الاشتراك
رسالة التفعيل خرجت من إجراء النموذج فوراً

ثم ضغطت سارة زر التفعيل، فمر الرابط على Mautic ليسجل النقرة، ووصلت إلى:

http://localhost:18798/activate.html?utm_source=mautic&utm_medium=email&utm_campaign=launch-oct&utm_content=activate
صفحة عربية عنوانها تم تفعيل حسابك وتحتها يمكنك الآن رفع أول فاتورة
صفحة التفعيل: هنا يقع التحويل

ماذا سجلت كل أداة؟

الآن نسأل كل أداة عما رأته. Shlink سجل نقرة واحدة على كل رابط، ومعها الوقت والمتصفح والصفحة المحالة منها Referer إذا وجدت، ويمكن قراءتها من الـ API:

curl -s "http://localhost:18796/rest/v3/short-urls/launch/visits" -H "X-Api-Key: $SHLINK_API_KEY"

وMautic أنشأ جهة اتصال لسارة بـ 20 نقطة، وسجل معاملات UTM مرتين، مرة لزيارة صفحة الهبوط ومرة لصفحة التفعيل:

+---------+------------+------------+--------------+-------------+---------------------+
| lead_id | utm_source | utm_medium | utm_campaign | utm_content | date_added          |
+---------+------------+------------+--------------+-------------+---------------------+
|       8 | linkedin   | social     | launch-oct   | post-1      | 2026-10-25 10:55:18 |
|       9 | x          | social     | launch-oct   | post-1      | 2026-10-25 10:55:26 |
|       9 | mautic     | email      | launch-oct   | activate    | 2026-10-25 10:55:38 |
|       9 | mautic     | email      | launch-oct   | activate    | 2026-10-25 10:55:39 |
+---------+------------+------------+--------------+-------------+---------------------+

في المخرج أعلاه لاحظ التالي:

  • جهة الاتصال رقم 8 هي زائر LinkedIn، وهو زائر مجهول Anonymous Visitor لم يكتب بريده، ولكن Mautic يحتفظ بمصدره، فإذا عاد بعد أسبوع وأرسل النموذج من نفس المتصفح ارتبط هذا السطر بجهة الاتصال.
  • سطر سارة الأول جاء من سكربت التتبع mtc.js عند فتح الصفحة، وليس من إجراء النموذج Record UTM Tags، والسبب في قسم أين ضاعت المعلومة.
  • سطرا التفعيل أحدهما من نقرة الرابط في الرسالة والآخر من زيارة الصفحة.

وسجل سارة في Mautic يرتب كل ذلك زمنياً، من الزيارة الأولى بمعاملات launch-oct حتى صفحة التفعيل:

سجل جهة الاتصال سارة في Mautic بأربعة عشر حدثاً: Contact created وAccessed from IP وUTM tags recorded وPage hit لصفحة الهبوط وForm submitted وContact identified وPoint gained بعشرين نقطة وEmail sent وEmail read ثم Page hit لصفحة التفعيل مع UTM tags recorded
سجل سارة في Mautic: 14 حدثاً في دقيقة واحدة

وفي Umami ظهر زائران وثلاث مشاهدات Views، ونسبة ارتداد Bounce Rate بخمسين في المائة، وهي زيارة LinkedIn التي لم تتجاوز الصفحة الأولى:

لوحة Umami لموقع منصة الفواتير: Visitors 2 وVisits 2 وViews 3 وBounce rate 50% وVisit duration 6s
Umami: زائران وثلاث مشاهدات

والقمع Funnel من ثلاث خطوات، طلبناه من الـ API: فتح الصفحة، ثم حدث trial-request، ثم حدث activate:

[{"type":"path","value":"/","visitors":2,"dropped":0},
 {"type":"event","value":"trial-request","visitors":1,"previous":2,"dropped":1,"dropoff":0.5},
 {"type":"event","value":"activate","visitors":1,"previous":1,"dropped":0,"dropoff":0}]

أي أن نسبة التحويل في هذه التجربة 50 في المائة، وهو رقم لا معنى له مع زائرين، ولكن نفس الاستعلام يعمل مع ألف زائر.

من يأخذ الفضل في التحويل؟

في تقرير الإسناد Attribution في Umami، الموثق في صفحة الإسناد، اخترنا نوع الخطوة Triggered event والحدث activate. ومع نموذج النقرة الأولى First Click كان الجواب X:

تقرير Attribution في Umami بنموذج First click ونوع Triggered event والخطوة activate: زائر واحد، والمصادر x والوسيط social والحملة launch-oct والمحتوى post-1 بنسبة 100%
النقرة الأولى: التحويل لمنشور X

ومع نموذج النقرة الأخيرة Last Click تغير الجواب إلى البريد:

نفس التقرير بنموذج Last click: المصدر mautic والوسيط email والحملة launch-oct والمحتوى activate بنسبة 100%
النقرة الأخيرة: التحويل لرسالة Mautic

وكلا الجوابين صحيح، فالمنشور جاء بسارة، والرسالة هي التي جعلتها تفعل الحساب. والذي يجمعهما أن الحملة launch-oct واحدة في الخطوتين، لأننا كتبناها بنفس القيمة في الرابط المختصر وفي إعدادات الرسالة، فإذا سألت «كم تحويلاً جلبت حملة الإطلاق؟» فالجواب واحد في النموذجين. لذلك اتفق مع فريقك على أسماء الحملات قبل الإطلاق، واجعل utm_campaign في رسائل Mautic هو نفس اسم الحملة التي جاء منها العميل، وإلا ظهر البريد في التقارير كأنه مصدر مستقل يأخذ الفضل من القنوات التي جلبت الناس فعلاً. والفرق بين النماذج بالتفصيل في مقال قياس التسويق.

أين ضاعت المعلومة؟

هذه أربعة أشياء ظهرت لنا أثناء التجربة، وكل منها قد يجعل أرقامك خاطئة دون أي رسالة خطأ.

المتصفحات الآلية لا تحسب

شغلنا التجربة أول مرة بمتصفح Chrome الآلي Headless، فوصل الزائران ووصلت الرسالة وفعلت سارة حسابها، ولكن Umami أظهر صفراً في كل شيء. والسبب أن Umami يفحص نوع المتصفح User-Agent بمكتبة isbot، فإذا كان آلياً رد بهذا الجواب ولم يسجل شيئاً، كما في كود الاستقبال:

{"beep":"boop"}

وفي نفس الوقت سجل Shlink الزيارتين ولكنه علمهما "potentialBot": true بحسب ميزة كشف البوتات. وهذا سلوك صحيح للزوار الحقيقيين، فهو يبعد البوتات عن تقاريرك، ولكن انتبه له إذا كنت تختبر المنظومة باختبارات آلية End-to-End: إما أن تعطي المتصفح User-Agent عادياً كما فعلنا في التشغيل الثاني، أو تفعل DISABLE_BOT_CHECK في إعدادات Umami على بيئة الاختبار فقط.

زائران من نفس الجهاز يظهران زائراً واحداً

في التشغيل الثاني أعطينا المتصفحين نفس User-Agent، فظهر في Umami زائر واحد وزيارة واحدة. والسبب أن Umami لا يستخدم Cookies، ويحسب معرف الجلسة Session من الموقع وعنوان IP ونوع المتصفح وقيمة سرية تتغير كل شهر افتراضياً، فزائران خلف نفس العنوان وبنفس المتصفح ونفس الإصدار يصبحان زائراً واحداً. وعندما جعلنا الزائر الأول iPhone ظهر زائران. وفي الواقع هذا يحدث في شركة كل موظفيها خلف عنوان واحد وبنفس المتصفح، فاعتبر عدد الزوار في أي أداة دون Cookies تقديراً وليس عداً دقيقاً.

إجراء Record UTM Tags في النموذج لم يسجل شيئاً

هذا الإجراء يقرأ معاملات UTM من عنوان الصفحة التي أرسلت النموذج، أي من ترويسة Referer. ولكن صفحة الهبوط على عنوان وMautic على عنوان آخر، والمتصفحات افتراضياً تطبق سياسة strict-origin-when-cross-origin الموضحة في توثيق Referrer-Policy، فترسل للموقع الآخر أصل الصفحة فقط دون المسار والمعاملات، وقد وجدنا في جدول Mautic أن الـ Referer المحفوظ هو http://localhost:18798/ فقط. لذلك لم يسجل الإجراء سطراً، وجاءت المعاملات من سكربت التتبع mtc.js الذي يرسل عنوان الصفحة كاملاً. والدرس أن تبقي سكربت التتبع في كل صفحة فيها نموذج، أو أن تضيف حقولاً مخفية Hidden Fields في النموذج تملؤها من عنوان الصفحة.

رسائل البريد تغير المصدر

كما رأينا في تقرير الإسناد، أي رابط في رسالة Mautic يحمل utm_source=mautic، فإذا لم تنتبه لاسم الحملة فيه صار البريد مصدراً جديداً في كل تقرير بالنقرة الأخيرة، وهو ما يحدث أيضاً مع أداة النشرات مثل listmonk إذا أضافت معاملاتها.

الموافقة وبيانات الزوار

هذه المنظومة تجمع بيانات شخصية: البريد والاسم في Mautic، وسجل الزيارات المرتبط بها. وعلى سيرفرك تبقى هذه البيانات عندك ولا تذهب لطرف آخر، ولكن هذا لا يغني عن الموافقة، فالمادة الخامسة والعشرون من نظام حماية البيانات الشخصية تشترط موافقة المتلقي على المواد الدعائية وآلية واضحة لإيقافها. لذلك ضع في النموذج موافقة صريحة على الرسائل التسويقية منفصلة عن طلب التجربة، واذكر في سياسة الخصوصية أن الموقع يستخدم Mautic لتتبع الزيارات بعد التعرف على الزائر. أما Umami فلا يستخدم Cookies كما في أسئلته الشائعة، ولا يربط الزيارة باسم أو بريد.

التحقق من النجاح

  • الرابط المختصر يرجع 302 إلى صفحة الهبوط ومعاملات UTM كاملة: curl -s -o /dev/null -w '%{http_code} %{redirect_url}' http://localhost:18796/launch.
  • صفحة UTM في Umami تظهر قيم utm_source التي استخدمتها، والأحداث trial-request وactivate تظهر في صفحة Events.
  • في Mautic تظهر جهة الاتصال الجديدة ومعها سطر UTM بمصدر الحملة، وليس فقط بمصدر البريد.
  • رسالة التفعيل تصل خلال ثوان من إرسال النموذج.
  • تقرير الإسناد للحدث activate يعطي مصدراً في النقرة الأولى وآخر في الأخيرة، كما في القسم السابق.

النسخ الاحتياطي Backup

في المنظومة ثلاث قواعد بيانات، ولكل منها أمرها:

docker exec mke-stack-umami-db-1 pg_dump -U umami umami | gzip > umami-$(date +%F).sql.gz
docker exec mke-stack-mautic-db-1 sh -c 'mysqldump --single-transaction --no-tablespaces -umautic -p"$MYSQL_PASSWORD" mautic' | gzip > mautic-$(date +%F).sql.gz
docker run --rm -v mke-stack_shlink-data:/d -v "$PWD":/backup nginx:1.31.6-alpine tar czf /backup/shlink-$(date +%F).tar.gz -C /d .

ومعها الـ Volumes الخاصة بإعدادات Mautic وملفاته كما في دليل Mautic، والملف .env ففيه كل الأسرار. ونسخ Shlink هنا نسخ لملف SQLite وهو يعمل، لذلك خذه في وقت هادئ أو أوقف Shlink لثوان، أو انقله إلى PostgreSQL في الإنتاج.

مشكلات شائعة وحلولها

Umami يظهر صفراً والصفحة فيها السكربت

افتح أدوات المطور في المتصفح وابحث عن الطلب /api/send، فإذا كان الرد {"beep":"boop"} فالمتصفح مصنف بوتاً، وإذا فشل الطلب فراجع أن عنوان Umami يعمل على HTTPS عندما يكون موقعك على HTTPS، وأن أداة حجب الإعلانات لا تحجبه.

إذا كانت من اختبار آلي أو من أداة مثل curl فهذا متوقع، أما إذا كانت من زوار حقيقيين فراجع أن الـ Reverse Proxy يمرر ترويسة User-Agent كما هي.

جهة الاتصال في Mautic بلا معاملات UTM

تأكد أن سكربت mtc.js موجود في صفحة الهبوط نفسها، وأن الرابط المختصر يحول إلى نفس الصفحة التي فيها النموذج، ولا تعتمد على إجراء Record UTM Tags وحده لما ذكرناه في قسم الـ Referer.

الحملة تظهر باسمين في التقارير

السبب اختلاف القيمة بين الرابط المختصر والرسالة، مثل launch-oct وLaunch_Oct، فاكتب أسماء الحملات بحروف صغيرة وشرطة، واحفظها في جدول مشترك قبل الإطلاق.

إزالة التجربة

docker compose down -v
docker rmi mautic/mautic:7.2.1-apache ghcr.io/umami-software/umami:3.4.1 shlinkio/shlink:5.1.8 \
  shlinkio/shlink-web-client:4.8.1 mysql:8.4.11 postgres:18.6 axllent/mailpit:v1.31.5 nginx:1.31.6-alpine

الخلاصة

وصلنا لنهاية الموضوع، وأهم ما فيه:

  • أربع أدوات مفتوحة المصدر في ملف Compose واحد، بحوالي 2.1 جيجابايت من الذاكرة، تجيب معاً عن سؤال لا تجيب عنه أي منها وحدها: من أين جاء العميل الذي فعل حسابه؟
  • الرابط بين الأدوات هو معاملات UTM، فاكتبها مرة واحدة في الرابط المختصر، واجعل اسم الحملة نفسه في رسائل البريد.
  • في تجربتنا أعطى نموذج النقرة الأولى الفضل لمنشور X، والنقرة الأخيرة لرسالة Mautic، وكلاهما صحيح لسؤال مختلف.
  • أربع مشكلات تعطيك أرقاماً خاطئة دون خطأ ظاهر: المتصفحات الآلية لا تحسب، والزوار خلف نفس العنوان والمتصفح يظهرون زائراً واحداً، وإجراء UTM في النموذج يفقد المعاملات بسبب سياسة الـ Referer، وروابط البريد تغير المصدر.
  • أرسل الرسائل العاجلة من إجراء النموذج، فقد وصلت رسالة التفعيل في أقل من ثانيتين.

والمقالات التي بنينا عليها هذا الدليل:

دليلقياس التسويق: روابط UTM والإسناد Attributionكيف تعرف القناة التي جاءت بكل عميل: روابط UTM بقواعد تسمية ثابتة، وتحويل تعرفه أنت، ونموذج إسناد تفهم ما يحسبه.دليلMautic على سيرفرك: حملة مؤتمتة من النموذج إلى البريدنشغل Mautic في Docker ونبني نموذجاً عربياً ونقاطاً للتقييم وحملة ترسل الترحيب وتنبه المبيعات، ونختبرها بزائرين تجريبيين.دليلالروابط المختصرة وتتبع النقرات: Bitly وبدائلهBitly وDub أمام Shlink وYOURLS على نطاقك، مع تجربة Shlink في Docker تبين لماذا لا يساوي عدد النقرات عدد الزوار.دليلتحليلات الموقع دون Google Analytics: مقارنة عمليةشغلنا Umami وPlausible وMatomo على نفس الموقع وقارناها بـ Google Analytics في حجم السكربت والـ Cookies والعربية وملكية البيانات.دليلأتمتة التسويق Marketing Automation وتقييم العملاء Lead Scoringكيف تتابع كل عميل محتمل في وقته بقواعد من محفز وشرط وإجراء، وكيف تقيمه بالنقاط لكي يبدأ فريق المبيعات بالأقرب للشراء.

سجل التحديثات

  • أكتوبر 2026: كتابة الدليل واختباره على Umami 3.4.1 (ghcr.io/umami-software/umami:3.4.1 مع postgres:18.6) وShlink 5.1.8 مع shlink-web-client 4.8.1 وMautic 7.2.1 (mautic/mautic:7.2.1-apache مع mysql:8.4.11) وaxllent/mailpit:v1.31.5 وnginx:1.31.6-alpine على Docker Engine 29.8.0.
نشرة عرب رووت | ArabRoot

معرفة تستحق مكاناً في بريدك.

مقالات مختارة وأدوات مفيدة وأفكار لمشروعك القادم، في رسالة واحدة كل أسبوع.

يمكنك إلغاء الاشتراك متى شئت. الخصوصية

تم استلام طلبك. افتح بريدك واضغط رابط التأكيد لإتمام الاشتراك.