تنظيم أسباب عدم وصول رسائل contact form - تصميم SPF / DKIM / DMARC وحقل From، ومعالجة كلّ حالة على حدة لـ external SMTP / shared hosting / PHP mail()
· 小村 豪 · email delivery, SPF, DKIM, DMARC, تحسين مسار الاستفسار, B2B
الملخّص التنفيذيّ
السبب الأكبر وراء أنّ رسائل الإشعار من contact form “تُرسَل بنجاح ولكنّها لا تصل” ليس اتّصال SMTP بحدّ ذاته، بل عدم تطابق المرسل الظاهر (From:) مع المرسل الذي يُصادَق فعليًّا (MAIL FROM في SPF و d= في DKIM). يعتمد DMARC على RFC5322.From بوصفه المرجع، وينظر فيما إذا كان أحد SPF أو DKIM يجتاز الفحص مع alignment. أي إنّ التصميم الذي يضع عنوان Gmail أو عنوان شركة المرسل في حقل From: كما هو، يبدو سهلًا للردّ، لكنّه في التشغيل الفعليّ تصميم سهل الفشل.
الخلاصة العمليّة بسيطة جدًّا. يجب تثبيت From: لرسائل الإشعار من contact form على نطاق الموقع نفسه، ووضع عنوان مستخدم النموذج في Reply-To: بوصف ذلك الأساس. يُستخدَم Sender: فقط حين “يختلف المؤلِّف عن جهة الإرسال الفعليّة”، أمّا Return-Path فلا يُكتَب يدويًّا في الـ header، بل يُضبَط من جانب الـ MTA أو خدمة البريد بوصفه envelope sender.
كذلك فإنّ التحويل (forwarding) يكسر SPF بسهولة، وعند تعديل النصّ أو الـ headers في mailing list أو في خادم وسيط ينكسر DKIM أيضًا. لذلك تحديدًا، الأسلم في contact form هو عدم الاعتماد على SPF فقط، بل تفعيل DKIM دائمًا، والبدء بمراقبة DMARC عبر p=none ثمّ التقدّم إلى quarantine / reject.
علاوةً على ذلك، تشترط Google للإرسال إلى Gmail ضبط SPF أو DKIM على نطاق الإرسال، وعند الإرسال بحجم معيّن فأكثر تجميع SPF و DKIM و DMARC جميعًا، وأن يتطابق نطاق حقل From: مع النطاق المُصادَق عبر SPF أو DKIM. الأقرب إلى الواقع أن نعتبر مشاكل التسليم مشكلة “تصميم مرسل” أكثر منها مشكلة “كود بريد”.
أدوار SPF و DKIM و DMARC وحقل From
في تسليم البريد، يجب الفصل بين envelope و header. ما يتحقّق منه SPF أساسًا هو MAIL FROM ضمن جلسة SMTP، وهو المرسل من ناحية التسليم. عند التسليم النهائيّ ينبغي أن يبقى مسارٌ عكسيّ واحد فقط بوصفه Return-Path، ولا ينبغي لنظام SMTP المُرسِل أن يصنع منذ البداية رسالةً تحمل header اسمه Return-Path. في المقابل، يمثّل From: على جانب headers الجسم “ممّن تبدو الرسالة”، و Reply-To: يمثّل وجهة الردّ، و Sender: يمثّل الجهة التي أرسلت فعليًّا.
يُعرّف RFC 5322 حقل From: بأنّه مؤلّف الرسالة، و Sender: بأنّه جهة الإرسال الفعليّة. إن كان المؤلّف وجهة الإرسال الفعليّة هما نفسهما، فلا ينبغي استخدام Sender:، وإن أردنا جعل وجهة الردّ مختلفة عن المؤلّف فالمنهج الصحيح استخدام Reply-To:. ويُصرّح RFC 5322 أيضًا بأنّه ينبغي عدم وضع عنوان لا يخصّ المؤلّف في From:. إشعار contact form عادةً ليس مرسلًا من المستخدم نفسه عبر MUA، بل إشعارٌ يصنعه نظام الموقع، فوضع عنوان المستخدم في From: ينحرف بسهولة عن دلالة المواصفة أيضًا.
DKIM يُلصِق توقيعًا على بعض headers والـ body، ويتحقّق منه المُستقبِل بمفتاحٍ عامّ في DNS. يُمثَّل نطاق التوقيع بـ d= ضمن DKIM-Signature، ويُجلَب المفتاح العامّ عبر selector كما في selector._domainkey.example.com. يُستخدَم DKIM بوصفه مصادقةً “أكثر صمودًا نسبيًّا أمام التحويل”، لكنّه إن عُدِّل الـ body أو headers الموقَّعة في الطريق، فستفشل bh الـ body hash والتحقّق من التوقيع.
DMARC لا يكتفي بنجاح SPF أو DKIM، بل ينظر فيما إذا كان النطاق المُصادَق منهما متّسقًا مع نطاق From:. هذه هي النقطة المحوريّة. إن نجح SPF بقيمة MAIL FROM=bounces.vendor.net وكان From: contact@example.com، فإنّ alignment الخاصّ بـ SPF يسقط. وإن نجح DKIM بـ d=example.com فيمكن أن يجتاز DMARC، لكن في غياب DKIM يفشل DMARC. يُعرِّف DMARC أوضاع alignment صارمة/مرنة عبر adkim / aspf، وسياسات p=none|quarantine|reject، ووجهات تقارير rua / ruf.
تُترجِم إرشادات Gmail الحديثة هذه الفلسفة التصميميّة مباشرةً إلى متطلّبات تشغيل. تُصرّح Google بأنّه “لا تنتحل header الـ From:“، وأنّه “في direct mail يجب أن يتطابق نطاق header الـ From: مع نطاق SPF أو نطاق DKIM”. إشعارات contact form ليست رسائل تسويقيّة، لكنّ منطق فلاتر الاستقبال الأساسيّ هو نفسه، فإذا تجاهلنا هذا المنطق ينخفض معدّل الوصول.
sequenceDiagram
participant User as Form user
participant App as Web app
participant SMTP as Sending MTA / SMTP service
participant DNS as DNS
participant MX as Receiving MX
User->>App: Submit form
App->>App: Decide From / Reply-To / Sender
App->>SMTP: SMTP send request
SMTP->>SMTP: Add DKIM signature
SMTP->>MX: MAIL FROM / RCPT TO / DATA
MX->>DNS: SPF lookup (MAIL FROM)
MX->>DNS: DKIM public key lookup (selector._domainkey)
MX->>DNS: DMARC lookup (_dmarc + From domain)
MX->>MX: Alignment check
MX-->>App: Receive / spam / reject / bounce
النقطة المهمّة في هذا المسار هي أنّ معيار حكم DMARC حتّى النهاية يدور حول نطاق From:. لأنّنا نلامس From: على جانب التطبيق، و MAIL FROM على جانب SMTP، و SPF/DKIM/DMARC على جانب DNS، كلٌّ على حدة، لا يحلّ إصلاحُ واحدٍ منها فقط مشكلة عدم الوصول.
سيناريوهات الفشل الشائعة في contact form
الأكثر شيوعًا هو وضع عنوان مستخدم النموذج في From:. مثلًا، عند الإرسال من SMTP الموقع وجعل From: taro@gmail.com، فإنّ ما يجتاز SPF أو DKIM عادةً هو نطاق الموقع، لا نطاق Gmail. والنتيجة انحراف يجعل From: على Gmail والنطاق المُصادَق هو example.com، فيفشل DMARC alignment. تطلب Google نفسها تجنّب انتحال From: وتطابق From: مع نطاق SPF/DKIM.
الثاني الأكثر شيوعًا هو انكسار SPF عبر التحويل. في تحويل البريد، يُصبح IP المرسِل المرئيّ من المُستقبِل النهائيّ غالبًا “خادمًا وسيطًا ليس مدرجًا في SPF لنطاق الإرسال الأصليّ”، فتسقط SPF حتّى للرسائل المشروعة. وتُرشِد Google أيضًا إلى أنّ “الرسائل المُحوَّلة عرضةٌ لفشل SPF، ولذلك يجب استخدام DKIM دائمًا”. إضافةً إلى ذلك، إن قام الوسيط بتعديل الـ body أو إضافة بادئة إلى الموضوع أو إلحاق footer، فسينكسر DKIM أيضًا.
نقص الإعدادات الأوّليّة عند استخدام خدمات external SMTP نموذجٌ شائع كذلك. في SendGrid قد لا يمكن الإرسال دون ضبط Domain Authentication، وعند تشغيل Automated Security تُولَّد سجلّات مصادقة بصيغة CNAME، ويمكن عند الحاجة ضبط Custom Return Path أو Custom DKIM Selector. في SES يُستخدَم افتراضيًّا MAIL FROM على amazonses.com، فيتحقّق SPF ضمنيًّا، لكن إن أردنا alignment لـ SPF مع نطاق الموقع، فيجب ضبط custom MAIL FROM. في Mailgun أيضًا، إن لم تُضبَط SPF/DKIM لنطاق الإرسال و MX اللازمة، لن يتكوّن توقيع إرسال صحيح.
MTA المحلّيّ ضمن shared hosting لغمٌ يَسهل إغفاله. حتّى لو كان موقعك مُصادَقًا، إن كان IP الفعليّ الخارج هو IP مشترَك سيّئ السمعة، أو لا يوجد PTR، أو لم يُضَف DKIM من جانب الـ hosting، فسينخفض معدّل الوصول. تُلزِم Google بـ PTR لـ IP الإرسال، وتُرشِد إلى أنّ سوء سمعة الـ IP المشترَك قد يكون سببًا لأخطاء من سلسلة 5.7.1.
طريقة استخدام mail() في PHP أو مكتبات الإرسال عرضةٌ للفشل أيضًا. يشرح دليل PHP أنّ mail() يحتاج إلى header اسمه From، وأنّه يمكن عبر معاملات إضافيّة تحديد envelope sender بـ sendmail -f. أي بدلًا من تركيب header اسمه Return-Path: بنفسك، مرّر envelope sender إلى الـ MTA. سوء فهم هذه النقطة يؤدّي إلى انحراف بين المرسل المُستخدَم في فحص SPF والمرسل الذي تفترضه التطبيقات.
أخيرًا، حالة العبث بـ Sender أو From في موضع ينبغي فيه استخدام Reply-To. إن كان هدفك توجيه الردّ إلى المستخدم فقط فإنّ Reply-To يكفي. أمّا Sender فيُستخدَم حين تريد توضيح أنّ “المؤلّف يختلف عن جهة الإرسال الفعليّة”، وليس header يُستخدَم بانتظام في contact form. الفصل بين هل غاية التصميم “إعادة الردّ إلى المستخدم” أم “إظهار الجهة المسؤولة عن الإشعار” يقلّل من الحوادث.
خطوات التشخيص والأوامر
أوّل ما ينبغي فعله هو النظر إلى headers البريد الخامّة قبل النظر إلى الكود. في Gmail عبر “إظهار مصدر الرسالة”، وفي Outlook عبر “تفاصيل الرسالة” أو “ترويسات الإنترنت”، يمكن التحقّق من Authentication-Results و Return-Path و From و Reply-To و DKIM-Signature و Received. النظر هنا يفصل بدقّة عالية بين هل المشكلة “لم يُرسَل” أم “انكسر اتّساق المصادقة”.
أوّل ما يُنظَر إليه في الـ headers
النقاط الخمس التالية ذات الأولويّة القصوى:
- ما هو نطاق
From: - ما هو نطاق
Return-Path: - هل يظهر
spf=pass/dkim=pass/dmarc=passفيAuthentication-Results: - عند
dkim=pass، ما هوheader.i=أوd=، وما هو نطاقهما - عند
dmarc=fail، هل السبب فشل المصادقة أم alignment failure
في دليل Google لاستكشاف أخطاء DMARC مذكور صراحةً أنّه حتّى لو اجتازت الرسالة المصادقات الأخرى، إن لم تكن الـ headers متّسقة (aligned) فسيفشل DMARC.
أوامر التحقّق من DNS
dig أداة تقليديّة موجّهة لاستكشاف أخطاء DNS، ويصفها دليل BIND بأنّها أداة استعلام DNS مرنة وذات إخراج واضح. nslookup أداة فحص أخفّ، يمكن استخدامها حتّى في الوضع غير التفاعليّ. للتحقّق من مصادقة contact form، نستعلم على الأقلّ عن SPF و DKIM و DMARC الثلاثة.
# SPF
dig +short TXT example.com
# DKIM
dig +short TXT form2026._domainkey.example.com
# DMARC
dig +short TXT _dmarc.example.com
# Windows
nslookup -type=txt example.com
nslookup -type=txt _dmarc.example.com
nslookup -type=txt form2026._domainkey.example.com
مثال الإخراج المتوقّع كالتالي:
"v=spf1 include:sendgrid.net include:mailgun.org ip4:203.0.113.10 -all"
"v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQ..."
"v=DMARC1; p=none; rua=mailto:dmarc-agg@example.com; ruf=mailto:dmarc-afrf@example.com; adkim=r; aspf=r; fo=1"
ما يجب النظر إليه هنا هو هل سجلّ SPF موحَّد في سجلّ واحد، وهل يمكن جلب المفتاح العامّ لـ DKIM، وهل يحتوي DMARC على p=. يُحدِّد RFC 7208 أنّ SPF يجب أن يحدّ آليّات إثارة استعلامات DNS بمجموع 10، وتجاوز الحدّ سببٌ لـ permerror.
التحقّق من اتّصال SMTP و TLS
openssl s_client عميل SSL/TLS عامّ من OpenSSL، مفيدٌ للتحقّق من STARTTLS وسلسلة الشهادات لخادم SMTP. عبر -starttls smtp يبدأ STARTTLS في SMTP، وعبر -showcerts يعرض قائمة الشهادات التي يعيدها الخادم.
printf 'QUIT\r\n' | openssl s_client \
-connect smtp.example.com:587 \
-starttls smtp \
-servername smtp.example.com \
-showcerts \
-brief
مثال على الإخراج:
CONNECTION ESTABLISHED
Protocol version: TLSv1.3
Ciphersuite: TLS_AES_256_GCM_SHA384
Verification: OK
250 CHUNKING
بهذا يمكن على الأقلّ رؤية هل يمكن الاتّصال بخادم SMTP، وهل STARTTLS مفعَّل، وهل توجد علل ظاهرة في التحقّق من الشهادة. تطلب Google TLS من المرسلين بحجم معيّن فأكثر، وعدم استخدام TLS قد يكون سببًا لـ 5.7.29.
اختبار إعادة إنتاج الإرسال الفعليّ
swaks أداة عمليّة مخصّصة لاختبار SMTP، تُعيد إنتاج اختبارات الإرسال شاملةً TLS والمصادقة وامتدادات SMTP بمرونة. في تحقيق عدم وصول contact form، من المفيد إرسال رسالة واحدة فقط “بنفس SMTP / نفس From / نفس Reply-To / نفس وجهة” دون المرور بالتطبيق.
swaks \
--server smtp.example.com \
--port 587 \
--tls \
--auth LOGIN \
--auth-user contact@example.com \
--auth-password '********' \
--from bounce@example.com \
--to yourtest@gmail.com \
--h-From "إشعار الموقع <contact@example.com>" \
--h-Reply-To "Yamada Taro <visitor@gmail.com>" \
--header "Subject: swaks test" \
--body "This is a test"
النموذج الاعتياديّ عند نجاح الإرسال:
=== Trying smtp.example.com:587...
=== Connected to smtp.example.com.
<- 250-STARTTLS
<- 250-AUTH LOGIN PLAIN
-> STARTTLS
<- 220 Ready to start TLS
...
<- 250 2.0.0 Ok: queued as ABC123DEF
إن فشل عبر التطبيق ونجح عبر swaks، فالاحتمال الأقوى أنّ السبب يميل إلى تركيب headers في المكتبة أو ضبط envelope sender. وعلى العكس، إن سقط swaks بالطريقة نفسها، فيمكن حصر السبب في DNS أو SMTP أو سياسات جهة الاستقبال.
التشخيص الخارجيّ عبر mail-tester
mail-tester خدمةٌ تُرسَل إليها رسالة على عنوان اختباريّ عشوائيّ، فتحلّل الرسالة وخادم الإرسال و IP الإرسال وتُعيد تقريرًا تفصيليًّا. مناسبةٌ كتشخيص أوّليّ لحالات “لا تصل لسبب غامض” في MTA المحلّيّ أو الخوادم المشتركة.
الاستخدام بسيط:
- الحصول على عنوان الاختبار الذي يُصدِره mail-tester
- إرسال رسالة واحدة بنفس مسار contact form
- الاطّلاع على النتيجة، وعلى ملاحظات SPF / DKIM / DMARC / reverse DNS / blocklist / تركيب الجسم
يجب عدم الحكم على التسليم في الإنتاج بناءً على نتيجة mail-tester وحدها، لكنّها على الأقلّ تكشف بسرعة “غياب SPF أصلًا” أو “تعذّر جلب المفتاح العامّ لـ DKIM” أو “تركيب الجسم والمرسل غير طبيعيّ”.
عيّنة لتحليل الـ headers
مثال للفشل:
Return-Path: <bounce-123@vendor.example.net>
Authentication-Results: mx.google.com;
spf=pass (google.com: domain of bounce-123@vendor.example.net designates 198.51.100.10 as permitted sender) smtp.mailfrom=vendor.example.net;
dkim=none;
dmarc=fail (p=quarantine sp=quarantine dis=none) header.from=gmail.com
From: Yamada Taro <visitor@gmail.com>
Reply-To: Yamada Taro <visitor@gmail.com>
Subject: استفسار
في هذه الرسالة، حتّى لو اجتاز SPF نفسه، فإنّ From: على gmail.com فيفشل DMARC. عطلٌ نموذجيّ ناتجٌ عن وضع عنوان المستخدم في From: في contact form.
مثال للنجاح:
Return-Path: <bounce@example.com>
Authentication-Results: mx.google.com;
spf=pass smtp.mailfrom=example.com;
dkim=pass header.i=@example.com header.s=form2026;
dmarc=pass header.from=example.com
From: Example Site <contact@example.com>
Reply-To: Yamada Taro <visitor@gmail.com>
Subject: إشعار استفسار
بهذا الشكل، يُؤمَّن سهولة الردّ على المستخدم عبر Reply-To:، ويتوحّد المرسل الظاهر والمرسل المُصادَق على example.com معًا، فيستقرّ معدّل الوصول كثيرًا.
أنماط تصميم From الموصى بها
المبدأ الذي لا يتزعزع في contact form هو توحيد “النطاق المستخدَم في المصادقة” مع “نطاق From الذي يُعرَض للمستلم”. وعلى ذلك، يُفلَت وجهة الردّ فقط إلى Reply-To:. يستقرّ التصميم بتقسيمه إلى ثلاث طبقات: استخدام Sender: فقط عند الحاجة، وضبط Return-Path عبر envelope.
| النمط | مثال headers | الحالة المناسبة | المزايا | تنبيهات |
|---|---|---|---|---|
| النمط الموصى به | From: contact@example.comReply-To: visitor@gmail.comReturn-Path: bounce@example.com |
تقريبًا جميع contact forms | يَسهل اجتياز DMARC / يَسهل الردّ / تنفيذ بسيط | إن نسيت Reply-To تذهب وجهة الردّ إلى الموقع |
| فصل subdomain | From: contact@form.example.comReply-To: visitor@gmail.comReturn-Path: bounce.form.example.com |
عند الرغبة بفصل إشعارات النموذج عن البريد الرئيسيّ | يَسهل فصل السمعة / يَسهل الإدارة | يجب ضبط SPF/DKIM/DMARC على جانب الـ subdomain أيضًا |
نمط بإظهار Sender |
From: contact@example.comSender: mailer@example.comReply-To: visitor@gmail.com |
متطلّبات خاصّة لإظهار جهة الإرسال | إظهار الجهة التشغيليّة المسؤولة | غير ضروريّ عادةً. زائد إن كان المؤلّف وجهة الإرسال متطابقَين |
| نمط غير موصى به | From: visitor@gmail.comReply-To: visitor@gmail.com |
تنفيذ يقتصر على إبراز وجهة الردّ | يبدو طبيعيًّا في المظهر فقط | سهل التسبّب في فشل DMARC. يُتجنَّب في إشعارات الاستفسار |
أساس هذا الجدول هو دلالات From / Sender / Reply-To في RFC 5322، ومعاملة Return-Path في RFC 5321، ومواصفة DMARC التي تحكم على الاتّساق بناءً على From:. الافتراضيّ لإشعارات النموذج يكفي معه “النمط الموصى به” في السطر الأوّل. حتّى في المواقف التي تشعر فيها برغبة وضع عنوان المستخدم في From:، يتحقّق الهدف بوضع وجهة الردّ في Reply-To:.
ما ينبغي تذكّره خصوصًا هو أنّ Return-Path ليس “header يُحرَّر”، بل “نتيجة envelope sender المستخدَم في التسليم”. في PHP mail() نتعامل معه عبر -f، وفي خدمات SMTP عبر إعدادات Custom MAIL FROM / Return Path / bounce domain، وهذا هو التطبيق الصحيح.
دليل الإعداد حسب التشكيلة
من هنا، ننظّم منطق الإعداد للأنماط الثلاثة الأكثر شيوعًا في الميدان. كأساس، القيمة الدقيقة التي تُدخَل في DNS، يجب الأخذ بأولويّة القيمة التي تُصدِرها لوحة إدارة كلّ خدمة. أمثلة السجلّات أدناه ممثِّلةٌ لفهم البنية.
حين يستخدم الموقع external SMTP
أهمّ نقطة في external SMTP هي إنهاء مصادقة نطاقك أوّلًا. تُرشِد Google أيضًا إلى أنّه عند استخدام email service provider يجب التحقّق من أنّ تلك الخدمة تُصادِق SPF و DKIM لنطاقك.
التشكيلة الموصى بها
From:هوcontact@example.comأوcontact@form.example.comReply-To:عنوان مستخدم النموذجReturn-Path/ MAIL FROM هو subdomain للـ bounce تديره أنت، مثلbounce.example.com- DKIM يوقَّع بـ
example.comأو بـ subdomain مخصّص للإرسال - DMARC يوضع على نطاق
From:الظاهر
إعدادات SendGrid النمطيّة
في SendGrid، Domain Authentication شرطٌ مسبق، وعند تفعيل Automated Security تُولَّد 3 سجلّات CNAME. عند إيقافه يُولَّد 1 MX و 2 TXT، ويمكن أيضًا ضبط Custom Return Path و Custom DKIM Selector.
; مثال: SendGrid (استخدم القيم المُولَّدة فعليًّا في لوحة الإدارة)
em123.example.com. CNAME u123456.wl.sendgrid.net.
s1._domainkey.example.com. CNAME s1.domainkey.u123456.wl.sendgrid.net.
s2._domainkey.example.com. CNAME s2.domainkey.u123456.wl.sendgrid.net.
_dmarc.example.com. TXT "v=DMARC1; p=none; rua=mailto:dmarc-agg@example.com"
ما يَسهل الوقوع فيه مع SendGrid هو حالة “اكتفاء بمصادقة SMTP فقط، دون ضبط Domain Authentication”. في هذه الحالة، يمكن الإرسال نفسه، لكن من جانب الاستقبال تضعف العلاقة بين From: والنطاق المُصادَق. الأساس هو إنجاز Domain Authentication، ثمّ ضبط Custom Return Path عند الحاجة فوقه.
إعدادات SES النمطيّة
يستخدم SES افتراضيًّا MAIL FROM على subdomain من amazonses.com، فيتحقّق SPF نفسه ضمنيًّا. لكن إن أردنا alignment لـ SPF مع نطاق الموقع، نستخدم custom MAIL FROM. في هذه الحالة يطلب SES من نطاق custom MAIL FROM TXT لـ SPF و MX، ويجب أن يكون MX واحدًا بالضبط. وفي Easy DKIM نضيف 3 CNAME إلى DNS.
; مثال: SES Easy DKIM
abcde12345._domainkey.example.com. CNAME abcde12345.dkim.amazonses.com.
fghij67890._domainkey.example.com. CNAME fghij67890.dkim.amazonses.com.
klmno54321._domainkey.example.com. CNAME klmno54321.dkim.amazonses.com.
; مثال: SES custom MAIL FROM
bounce.example.com. MX 10 feedback-smtp.ap-northeast-1.amazonses.com.
bounce.example.com. TXT "v=spf1 include:amazonses.com -all"
_dmarc.example.com. TXT "v=DMARC1; p=none; rua=mailto:dmarc-agg@example.com"
النقطة المهمّة في تصميم SES هي أن يكون نطاق MAIL FROM ليس نطاق From: المرسِل نفسه، بل subdomain مخصّص للـ bounce. ترشِد AWS أيضًا إلى أن يكون MAIL FROM subdomain ليس هو نطاق الإرسال الفعليّ نفسه.
إعدادات Mailgun النمطيّة
في Mailgun، عند التحقّق من نطاق الإرسال نحتاج إلى TXT لـ SPF و TXT لـ DKIM، إضافة إلى MX اثنين. إن وُجد SPF مسبقًا، فبدلًا من إضافة سجلّ SPF جديد، نُدخِل include:mailgun.org ضمن السجلّ الموجود. قد تظهر مفاتيح DKIM متعدّدة، لكن إن كان المفتاح المستخدَم حاليًّا منشورًا بصورة صحيحة في DNS فالإرسال ممكن.
; مثال: استخدام Mailgun على subdomain
mg.example.com. TXT "v=spf1 include:mailgun.org ~all"
mx._domainkey.mg.example.com. TXT "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQ..."
mg.example.com. MX 10 mxa.mailgun.org.
mg.example.com. MX 10 mxb.mailgun.org.
email.mg.example.com. CNAME mailgun.org.
_dmarc.example.com. TXT "v=DMARC1; p=none; rua=mailto:dmarc-agg@example.com"
Mailgun متناسبٌ مع التشغيل عبر subdomain، فبتقطيع subdomain إرسال مخصّص مثل mg.example.com تَسهل إدارة إشعارات النموذج وإشعارات المعاملات.
حين يُرسل الموقع عبر MTA المحلّيّ في shared hosting
أوّل ما يجب النظر إليه في shared hosting هو جودة بنية الإرسال على جانب الـ hosting، لا تطبيقك. إن كان PTR لـ IP الإرسال، ودعم DKIM، وسمعة الـ IP المشترَك، ووضوح سجلّات الإرسال ضعيفة، فهذه التشكيلة في حدّ ذاتها مكلَّفة. تُولِي Google أهمّيّة لـ PTR لـ IP الإرسال، وتُرشِد إلى أنّ سوء سمعة الـ IP المشترَك قد يكون سببًا للحجب.
من الناحية العمليّة، الترتيب الآمن هو الآتي:
- التحقّق من أنّ شركة الـ hosting تتيح ضبط SPF/DKIM/PTR عبر لوحة الإدارة أو الدعم
- تثبيت
From:دائمًا على نطاقك - تضمين IP الإرسال للـ hosting أو نطاق الإرسال المسموح به في SPF
- تفعيل DKIM عبر ميزة الـ hosting. إن لم تكن متوفّرة، التبديل إلى external SMTP
- إن أمكن، فصل عنوان bounce لـ MAIL FROM إلى مثل
bounce.example.com
كنموذج لإنشاء DKIM بنفسك في shared hosting، تُتيح أنظمة من نوع OpenDKIM توليد المفتاح الخاصّ وسجلّ TXT لـ DNS عبر opendkim-genkey. بنية وضع المفتاح العامّ لـ DKIM في selector._domainkey.example.com المرفقة بالـ selector تتطابق مع RFC 6376.
mkdir -p /etc/opendkim/keys/example.com
opendkim-genkey -D /etc/opendkim/keys/example.com -d example.com -s form2026
chown opendkim:opendkim /etc/opendkim/keys/example.com/form2026.private
chmod 600 /etc/opendkim/keys/example.com/form2026.private
صورة ما بعد التوليد كالتالي:
form2026._domainkey.example.com. TXT "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQ..."
# /etc/opendkim/KeyTable
form2026._domainkey.example.com example.com:form2026:/etc/opendkim/keys/example.com/form2026.private
# /etc/opendkim/SigningTable
*@example.com form2026._domainkey.example.com
لكن إن كنت لا تستطيع التحكّم بنفسك في PTR أو outbound relay في shared hosting، فالأقصر هو الانتقال إلى external SMTP. حتّى للإرسال بكمّيّة قليلة كإشعارات contact form، فإنّ MTA محلّيًّا ضعيف المصادقة في وضعٍ غير مؤاتٍ أمام Gmail وبريد الشركات.
حين يستخدم الموقع PHP mail() أو مكتبة SMTP
PHP mail() مريح، لكن من زاوية المصادقة ومعدّل الوصول، تعتمد على هويّة الـ MTA الذي خلفها. يشرح دليل PHP أنّ البريد يحتاج إلى header اسمه From، وأنّه عند الإرسال عبر sendmail_path يمكن تحديد envelope sender بمعاملات إضافيّة. بمعنى مقلوب، استخدام mail() لا يضبط SPF/DKIM/DMARC تلقائيًّا.
أوّلًا، نصمّم بالحدّ الأدنى كالتالي:
From:هوcontact@example.comReply-To:هو مستخدم النموذج- envelope sender هو
bounce@example.com - إدراج عنوان المستخدم بوضوح في الجسم أيضًا
- إن أمكن، استخدام SMTP مُصادَق بدلًا من
mail()
مثال على أبسط تشكيلة لـ mail()
إدخال مدخلات المستخدم في headers كما هي خطر. إن خُلِط CR/LF في $name أو $email يستطيع المهاجم حقن headers إضافيّة مثل Bcc:، فيصبح النموذج مرحِّلًا للـ spam. يُرشِد دليل PHP أيضًا إلى التحقّق/التطبيع الإلزاميّ للمدخلات الخارجيّة المستخدَمة في headers. في المثال أدناه، يُنقَّى أيّ شيء يُحقَن في headers سلفًا، شاملًا وسيط envelope sender (additional_params).
<?php
// تُعيد فقط القيم الصالحة للاستخدام في header. ترفض إن وُجد CR/LF/NUL.
function sanitize_header_value(string $value): string {
if (preg_match('/[\r\n\0]/', $value)) {
throw new InvalidArgumentException('Invalid characters in header value');
}
return trim($value);
}
// تتحقّق من email بحسب RFC.
function sanitize_email(string $email): string {
$clean = sanitize_header_value($email);
if (!filter_var($clean, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException('Invalid email address');
}
return $clean;
}
$to = 'ops@example.com';
$subject = sanitize_header_value('إشعار استفسار');
// $name / $email / $message مدخلات نموذج. $message للجسم فيُسمَح فيه CR/LF،
// لكن $name / $email المُستخدَمَين في headers يُرفَض فيهما CR/LF دائمًا.
$safeName = sanitize_header_value($name);
$safeEmail = sanitize_email($email);
$body = <<<TEXT
Name: {$safeName}
Email: {$safeEmail}
{$message}
TEXT;
$headers = [
'From' => 'Example Site <contact@example.com>',
'Reply-To' => sprintf('%s <%s>', $safeName, $safeEmail),
'Content-Type' => 'text/plain; charset=UTF-8',
];
// additional_params يمرّ أيضًا إلى shell، فاستخدم قيمًا ثابتة فقط ولا تَخلِط مدخلات ديناميكيّة.
mail($to, $subject, $body, $headers, '-fbounce@example.com');
النقطتان في هذا المثال: الأولى أنّ Return-Path: لم يُكتَب بوصفه header، بل مُرِّر envelope sender عبر -f في الوسيط الخامس. والثانية أنّ مدخلات المستخدم المُحقَنة في headers مثل Reply-To: تمرّ عبر دالّة تنقية ترفض CR/LF. إن رُكِّبت headers بدون تنقية يمكن للمهاجم حقن سلسلة مثل \r\nBcc: victim@example.com لإدراج headers إضافيّة، ولذلك يُلزِم دليل PHP بالتحقّق من المدخلات الخارجيّة المستخدَمة في headers. ما additional_params يمرّ في النهاية إلى shell أيضًا، فمرّر قيمًا ثابتة لا تَخلط فيها مدخلات المستخدم.
مثال باستخدام مكتبة SMTP
<?php
$mail->isSMTP();
$mail->Host = 'smtp.example.com';
$mail->Port = 587;
$mail->SMTPAuth = true;
$mail->SMTPSecure = 'tls';
$mail->Username = getenv('SMTP_USER');
$mail->Password = getenv('SMTP_PASS');
$mail->setFrom('contact@example.com', 'Example Site');
$mail->addAddress('ops@example.com');
$mail->addReplyTo($email, $name);
// قد تتيح بعض المكتبات ضبط Sender / return-path بشكل منفصل
$mail->Sender = 'bounce@example.com';
$mail->Subject = 'إشعار استفسار';
$mail->Body = $body;
$mail->send();
ميزة مكتبات SMTP أنّه يَسهل التحكّم بمرسل الـ header ومرسل الـ envelope بشكل منفصل. لاستخدام contact form، الأنسب هو تصميم يثبّت From: على نطاق الموقع ويضع وجهة الردّ فقط في Reply-To:.
أمثلة عيانيّة لـ SPF و DKIM و DMARC
مثال أساسيّ لـ SPF
example.com. TXT "v=spf1 ip4:203.0.113.10 include:sendgrid.net -all"
في SPF يجب تضمين كلّ مصادر الإرسال الفعليّة. عند استخدام مرسلين خارجيّين، تطلب Google أيضًا التحقّق من أنّ هذا المرسِل يصادق SPF و DKIM. ولأنّ SPF لديه حدّ على عدد استعلامات DNS، فالحذر من المبالغة في تكديس include.
مثال أساسيّ لـ DKIM
form2026._domainkey.example.com. TXT "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQ..."
يُستخدَم selector لتدوير المفاتيح، ويسمح بتعايش مفاتيح عامّة متعدّدة على نفس النطاق. في التشغيل، أسماءٌ تميِّز بحسب الاستخدام أو السنة والشهر تكون أوضح لاحقًا من default.
مثال على إدخال DMARC
نبدأ بوضع المراقبة:
_dmarc.example.com. TXT "v=DMARC1; p=none; rua=mailto:dmarc-agg@example.com; ruf=mailto:dmarc-afrf@example.com; adkim=r; aspf=r; fo=1"
ثمّ حين نريد عزل جزء:
_dmarc.example.com. TXT "v=DMARC1; p=quarantine; pct=25; rua=mailto:dmarc-agg@example.com; adkim=r; aspf=r"
وأخيرًا، التشغيل الصارم:
_dmarc.example.com. TXT "v=DMARC1; p=reject; sp=reject; rua=mailto:dmarc-agg@example.com; adkim=s; aspf=s"
p=none يعني المراقبة فقط، و quarantine يعني توصية بالعزل، و reject يعني توصية بالرفض أثناء SMTP. تتبدّل adkim و aspf بين strict/relaxed. أأمن من القفز مباشرةً إلى reject هو مراقبة التدفّق ومصادر الإرسال المشروعة بـ none، ثمّ الترقّي تدريجيًّا.
علاوة على ذلك، عند إرسال rua / ruf إلى خدمة تجميع خارجيّة، يحتاج RFC 7489 إلى سجلّ DNS إضافيّ على جانب الطرف الثالث. مثلًا لإرسال تقارير example.com إلى thirdparty.example.net، يجب أن ينشر جانب الاستقبال example.com._report._dmarc.thirdparty.example.net TXT "v=DMARC1".
قائمة تحقّق الـ troubleshooting
أخيرًا، نلخّص ترتيبًا للتحقّق يصلح للاستخدام الميدانيّ كما هو. عدم وصول بريد contact form يَسرع حلّه بسحقه بالترتيب التالي:
ما يجب التحقّق منه أوّلًا
- هل
From:على نطاقك - هل عنوان المستخدم موضوع في
Reply-To: - هل في
Authentication-Resultsيظهرspf=passأوdkim=pass، وdmarc=pass - إن كان
dmarc=fail، هل بسبب فشل المصادقة أم alignment failure - هل SPF موحَّد في سجلّ واحد
- هل عدد استعلامات SPF ليس مفرطًا
- هل يمكن جلب المفتاح العامّ لـ DKIM
- هل DMARC يحتوي على
p= - هل PTR و reverse DNS لـ IP الإرسال معقولان
- هل لا تستخدم IP مشترَكًا، أو هل لم تتدهور سمعته
مكان النظر في الـ bounces والـ logs
إن وصلت bounces، فما يهمّ في DSN بصيغة message/delivery-status هو Final-Recipient و Status و Action و Diagnostic-Code. يُعرِّف RFC 3464 معلومات فشل التسليم القابلة للقراءة الآليّة هذه. مثلًا إن وُجد سطر مثل Diagnostic-Code: smtp; 550 relay not permitted، فهو رفضٌ على جانب SMTP لا على طبقة التطبيق.
على جانب الخادم، نفحص على الأقلّ logs تسليم الـ MTA. في Postfix يَظهر نجاح/فشل التسليم، وتراكم الـ queue، ورفض الـ relay، وفشل استبيان DNS، وتنبيهات DKIM milter. وفي Exim مثل ذلك. أوامر الفحص النموذجيّة:
# مثال: Postfix
journalctl -u postfix -n 200 --no-pager
postqueue -p
# مثال: Exim
exim -bp
تختلف مسارات الـ logs وصلاحيّات الأوامر بحسب الـ hosting، فتحقّق أوّلًا من إمكانيّة رؤية “logs تسليم البريد” لا “logs التطبيق”. في بيئة لا ترى فيها هذه، التحوّل إلى external SMTP يُسهّل تحليل المشكلات.
كيفيّة النظر في Gmail و Outlook
في Gmail عبر “إظهار مصدر الرسالة”، وفي Outlook من Microsoft عبر “تفاصيل الرسالة” أو “ترويسات الإنترنت”، يمكن التحقّق من headers خامّة. في تحقيق عدم وصول contact form، يَفيد حفظ النصّ الكامل لـ headers ومقارنته، أكثر من screenshots.
تمييز أخطاء جانب الاستقبال
أخطاء سلسلة Gmail سهلة قراءة السبب نسبيًّا.
| مثال خطأ | المعنى | المعالجة الأساسيّة |
|---|---|---|
5.7.27 |
فشل SPF | إضافة المرسل إلى سجلّ SPF |
5.7.30 |
فشل DKIM | إصلاح مفتاح/توقيع DKIM |
4.7.32 |
عدم اتّساق نطاق المؤسّسة بين From: و SPF/DKIM |
مراجعة تصميم From: |
5.7.25 |
خلل في PTR / reverse DNS | ضبط reverse DNS لـ IP الإرسال |
تُوضّح Google أيضًا في FAQ هذه الأخطاء وسياسة المعالجة. الأكثر شيوعًا في contact form هو 4.7.32 عدم تطابق alignment.
المعيار الأخير للحكم
إن استوفيت الشروط الثلاثة الآتية معًا، فتصميم إشعار contact form متين جدًّا:
From:ضمنexample.comReply-To:عنوان مستخدم النموذج- ظهور
dmarc=passفيAuthentication-Results
إن اجتمعت هذه الثلاثة، فأيًّا كنت تستخدم من SendGrid أو SES أو Mailgun أو shared hosting أو مكتبة SMTP، يكون التصميم منطقيًّا. وعلى العكس، إن نقص واحد منها فقط، فالأقصر الشكّ أوّلًا في تصميم From:.
مقالات ذات صلة
أحدث المقالات التي تشترك في نفس الوسوم. عمّق فهمك بمواضيع مرتبطة.
لماذا يُعدّ PPAP ممارسة سيّئة لأمن البريد الإلكترونيّ، وما البديل الصحيح؟
يبيّن المقال لماذا يُضعف نمط PPAP السرّيّة ولا يمنع الإرسال الخاطئ ويعيق فحص البرمجيّات، ويعرض بديلاً عمليّاً عبر TLS و S/MIME والتنزيل ا...
كيف يمكن للشركات الصغيرة والمتوسّطة تصميم البريد الجماعيّ دون أن تحبس نفسها مع مزوّد واحد
كيف ترسل شركة صغيرة أو متوسّطة بريداً جماعيّاً موثوقاً من نطاقها دون قيد المزوّد، عبر إرسال فرديّ، حالة اشتراك، إلغاء تلقائيّ، و SPF/DKIM...
كيف تبني صفحة خدمة لشركات B2B التقنيّة
كيف تبنى صفحة خدمة لشركات B2B التقنيّة لتعمل بوصفها مدخل استفسار حقيقياً: حدِّد الدور، اعتمد بنية بسيطة، رتِّب العناوين وفق قرار القارئ، ...
كيف نُطيل عمر أنظمة الويب الداخليّة المعتمدة على IE mode وكيف نخرج منها - تنظيم الاستراتيجيّات الميدانيّة من الإدارة المركزيّة لقائمة المواقع، إلى WebView2، والإعادة الهيكليّة التدريجيّة، ووصولًا إلى عزل VDI
نتناول استراتيجيّةً عمليّةً لإطالة عمر أنظمة الويب الداخليّة المعتمدة على IE mode والخروج منها تدريجيًّا عبر إدارة قائمة المواقع وتغليف W...
ما يجب التحقّق منه عندما لا يعمل ActiveX على Office 2024 / Microsoft 365 - الترتيب العمليّ لتغطية التعطيل الافتراضيّ، 32bit / 64bit، تسجيل COM، DLL التابعة، ووصولًا إلى IE mode
دليل عمليّ لتشخيص توقّف ActiveX على Office 2024 و Microsoft 365، يرتّب الفحوص من التعطيل الافتراضيّ إلى تطابق 32bit / 64bit وتسجيل COM وD...
أين يتصل هذا الموضوع
ترتبط هذه المقالة بشكل طبيعي بصفحات الخدمات التالية.
تطوير الموقع الإلكتروني
نرتّب الصفحة الرئيسية وصفحات الخدمة وصفحات الشركة ومسار الاستفسار، حتى يفهم الزائر ما تقدّمه الشركة.
الملف الشخصي للمؤلف
صفحة الملف الشخصي لمؤلف المقالة.
غو كومورا
مؤسّس شركة كومورا سوفت ذ.م.م.
يركّز على تطوير برامج ويندوز، والاستشارات التقنية، والتحقيق في الأخطاء، ويتميّز في المشاريع التي تبقى فيها الأصول القديمة ناشطة، وفي تشخيص الأعطال التي يصعب تحديد سببها.
روابط عامة