کلید شما به دامنهتان قفل است. این یعنی حتی اگر کسی کلید را از سورس صفحهٔ شما بردارد، از سایت خودش نمیتواند با آن درخواست بفرستد. این قفل دو نیمه دارد: اثبات مالکیت (یکبار، هنگام راهاندازی) و بررسی مبدأ (در هر درخواست).
۱. اثبات مالکیت دامنه
تا مالکیت دامنهٔ اصلی اپ تأیید نشود، کلید 403 domain_not_verified برمیگرداند. توکن تأیید و مقادیر دقیق در صفحهٔ اپ در پنل نمایش داده میشود. یکی از دو روش:
روش DNS (پیشنهادی)
یک رکورد TXT روی هاست _netarz.<دامنه> بسازید:
# نام رکورد (Host) نوع مقدار
_netarz.example.com. TXT "netarz-fx-verify=ntz-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
انتشار DNS معمولاً چند دقیقه طول میکشد (گاهی تا چند ساعت). با dig میتوانید ببینید رکورد دیده میشود یا نه:
dig +short TXT _netarz.example.com
# "netarz-fx-verify=ntz-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
در پنلهای DNS ایرانی و Cloudflare، در فیلد «Name» فقط _netarz را بنویسید؛ دامنه خودکار اضافه میشود. رکورد را «DNS only» (بدون پروکسی) بگذارید.
روش فایل
یک فایل متنی با محتوای توکن روی سایت بگذارید. باید با https (یا http) در دسترس باشد. آدرس با www و بدون آن هر دو بررسی میشوند و ریدایرکت داخل خودِ دامنه دنبال میشود؛ ریدایرکت به دامنهٔ دیگر پذیرفته نیست:
# مسیر: https://example.com/.well-known/netarz-fx-verify.txt
# محتوا: (فقط توکن، بدون چیز دیگر)
ntz-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
توکن را دستی تایپ نکنید. یک کاراکتر اشتباه — معمولاً 1 بهجای i یا l — کافی است تا تأیید انجام نشود، در حالی که فایل بهظاهر درست باز میشود. در صفحهٔ اپ دکمهٔ «دانلود فایل آماده» همان فایل را با محتوای درست به شما میدهد؛ همان را بدون تغییر آپلود کنید.
# nginx — اگر مسیر .well-known را جای دیگری هندل میکنید
location = /.well-known/netarz-fx-verify.txt {
default_type text/plain;
return 200 "ntz-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
}
// Laravel — routes/web.php
Route::get('/.well-known/netarz-fx-verify.txt', fn () => response('ntz-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 200, ['Content-Type' => 'text/plain']));
پس از هر کدام، در پنل «بررسی و تأیید» را بزنید. تأیید یکبار برای همیشه است؛ بعداً میتوانید رکورد یا فایل را حذف کنید.
دامنههای توسعه
localhost، 127.0.0.1 و دامنههای .test / .local / .localhost خودکار تأیید میشوند — چون مالکیتشان قابل اثبات نیست — و همیشه روی طرح رایگان میمانند.
۲. بررسی مبدأ در هر درخواست
قانون ساده است:
- اگر درخواست هدر
Origin(یاReferer) دارد — یعنی از مرورگر آمده — هاست آن باید با دامنههای اپ بخواند. - اگر ندارد — یعنی از سرور، cURL یا ربات آمده — IP درخواست باید در فهرست IPهای مجاز اپ باشد.
هر کدام که برقرار باشد کافی است. مرورگرها هدر Origin را نمیگذارند جعل کنید، و IP سرور در دست شماست.
قواعد دامنه
example.com # دقیق — www.example.com هم پذیرفته میشود
app.example.com # یک زیردامنهٔ مشخص
*.example.com # همهٔ زیردامنهها (و خود example.com)
دامنههای اضافی نیاز به تأیید جداگانه ندارند؛ فرض بر این است که مالک دامنهٔ اصلی، زیردامنهها و دامنههای کمکی خودش را اضافه میکند.
قواعد IP
185.12.34.5 # IP دقیق (IPv4 یا IPv6)
185.12.34. # پیشوند: هر IP که با این شروع شود
185.12.34.0/24 # CIDR (IPv4)
IP خروجی سرور شما مهم است (آنچه نِت اَرز میبیند)، نه IP دامنه. با curl https://api.ipify.org از روی سرور پیدایش کنید. اگر سرور شما IP ثابت ندارد (هاست اشتراکی، Cloudflare Workers، Vercel یا Google Sheets)، راهنمای قفل دامنه و IP را در گیتهاب بخوانید.
CORS
برای درخواست از مرورگر، هدرهای CORS فقط برای دامنههای مجاز اپ برگردانده میشوند. Preflight (OPTIONS) همیشه ۲۰۴ میدهد — چیزی افشا نمیکند — و درخواست اصلی است که با قفل دامنه سنجیده میشود:
OPTIONS /api/fx/v1/rates HTTP/1.1
Origin: https://example.com
Access-Control-Request-Headers: authorization
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Headers: Authorization, X-API-Key, Content-Type, Accept
Access-Control-Allow-Methods: GET, POST, PATCH, DELETE, OPTIONS
Access-Control-Max-Age: 86400
هدرهای X-Quota-*، X-FX-Plan و X-RateLimit-* با Access-Control-Expose-Headers برای JavaScript شما قابل خواندناند.
خطاهای قفل دامنه
| code | چه شده | چه کنید |
|---|---|---|
domain_not_verified | مالکیت دامنهٔ اصلی تأیید نشده. | DNS یا فایل، سپس «بررسی» در پنل. |
origin_not_allowed | Origin/Referer با دامنههای اپ نمیخواند. پاسخ فیلد origin و allowed را برمیگرداند. | دامنهٔ صفحه را به «دامنههای اضافی» اضافه کنید. |
origin_required | بدون Origin/Referer و بدون هیچ IP مجازی. | IP سرور را در پنل مجاز کنید. |
ip_not_allowed | IP درخواست در فهرست نیست (فیلد ip در پاسخ). | همان IP را اضافه کنید. |
درخواستهای ردشده را در پنل ببینید
هر درخواستی که قفل دامنه ردش کند، در صفحهٔ اپ زیر عنوان «درخواستهای ردشده» ثبت میشود: زمان، علت، IP و دامنهٔ فرستنده. این درخواستها از سهمیهٔ روزانهٔ شما کم نمیشوند.
اگر شمارندهٔ ردشده بالا میرود، درخواست شما به ما میرسد و فقط قفل دامنه جلویش را میگیرد؛ همان IP یا دامنه را در پنل اضافه کنید. اگر هیچ ردشدهای ثبت نمیشود و مصرف هم تکان نمیخورد، درخواست اصلاً به ما نرسیده و باید شبکهٔ سرور خودتان را بررسی کنید.