خطای ip_not_allowed در API نرخ ارز؛ قفل IP، دام IPv6 و راهحل Google Sheets
بیشتر پیامهایی که دربارهٔ API نرخ ارز به پشتیبانی میرسد با یکی از این دو جمله شروع میشود: «۴۰۳ میگیرم» یا «IP را ثبت کردهام ولی باز خطا میدهد». علت تقریباً همیشه یکی است: کلید به دامنه و IP شما قفل است و درخواست از جایی رسیده که روی اپ ثبت نشده. این قفل عمداً سختگیر است، چون کلیدی که لو برود، بدون آن با سهمیهٔ شما از هر جای دنیا کار میکند.
این راهنما قاعدهٔ قفل را دقیق میگوید، از متن خطا شروع میکند و برای هر نوع میزبان، از سرور مجازی تا Google Sheets، راه درست را نشان میدهد. مرجع رسمی همین قاعده صفحهٔ قفل دامنه در مستندات است.
قاعده در یک جمله: مرورگر با دامنه، سرور با IP
| درخواست از کجا آمده | نِت اَرز چه چیزی را میسنجد | کجا تنظیم میشود |
|---|---|---|
| مرورگر (سربرگ Origin یا Referer دارد) | دامنهٔ صفحه | دامنهٔ اصلی و «دامنههای اضافی» اپ |
| سرور، cURL، ربات، اسکریپت (بدون Origin) | IP فرستنده | «IPهای مجاز» اپ |
پیش از هر دو، مالکیت دامنهٔ اصلی اپ باید یک بار تأیید شده باشد. دامنههای توسعه مثل localhost خودکار تأیید میشوند. ربات تلگرام یا اسکریپتی که روی هیچ دامنهای نیست هم به یک دامنهٔ تأییدشده نیاز دارد؛ لازم نیست ربات روی همان دامنه اجرا شود، فقط اپ باید دامنهای داشته باشد که مال شماست.
از متن خطا شروع کنید
- domain_not_verified: تأیید مالکیت انجام نشده. فایل آماده را از صفحهٔ اپ دانلود کنید و بدون ویرایش بارگذاری کنید. فایلی که دستی تایپ شده، باز میشود و درست به نظر میرسد، ولی تأیید نمیشود.
- origin_not_allowed: صفحه روی دامنهای است که روی اپ نیست. پاسخ خطا فیلد
originدارد؛ همان را به «دامنههای اضافی» اضافه کنید. نسخهٔ www رایجترین جاافتاده است. - origin_required: درخواست از سرور آمده و اپ هنوز هیچ IP مجازی ندارد.
- ip_not_allowed: IP فرستنده در فهرست نیست.
در دو خطای آخر، پاسخ فیلدی به نام ip دارد: همان آدرسی که نِت اَرز دیده است. مطمئنترین منبع همین است، مطمئنتر از هر سایت «IP من چیست». دقیقاً همان را ثبت کنید. در صفحهٔ اپ در پنل هم بخش «درخواستهای ردشده» زمان، علت، IP و دامنهٔ هر درخواست ردشده را نشان میدهد، و این درخواستها از سهمیهٔ روزانه کم نمیشوند.
پیش از اینکه به کد برنامه دست بزنید، این آزمون چندثانیهای را روی خود سرور اجرا کنید. سه دستور، سه جواب روشن:
# 1) the key, the app and today's quota, over IPv4
curl -sS -4 https://netarz.ir/api/fx/v1/me -H "Authorization: Bearer $NETARZ_FX_KEY"
# 2) this server's outgoing addresses
curl -4 https://api.ipify.org; echo
curl -6 https://api64.ipify.org; echo
# 3) the same call without -4: if this one fails and (1) worked, it is the IPv6 trap
curl -sS https://netarz.ir/api/fx/v1/me -H "Authorization: Bearer $NETARZ_FX_KEY"
اگر دستور اول نام اپ و سهمیه را برگرداند، کلید و IPv4 درستاند و مشکل در تنظیم برنامهٔ شماست. اگر خطا داد، فیلد ip جوابش را با خروجی دستور دوم مقایسه کنید. و اگر اولی کار کرد و سومی نه، بخش بعد دقیقاً دربارهٔ شماست.
دام IPv6: IP را ثبت کردهاید و باز رد میشوید
این یکی بیشترین وقت را میگیرد. netarz.ir هم آدرس IPv4 دارد و هم IPv6. سروری که هر دو نسخه را دارد، معمولاً با IPv6 وصل میشود، در حالی که دستور معروف curl https://api.ipify.org فقط IPv4 را نشان میدهد. نتیجه: IPv4 را ثبت کردهاید و ما IPv6 را میبینیم.
پیشنهاد ما این است که درخواست را روی IPv4 نگه دارید، چون IPv6 سرورها گاهی عوض میشود:
cURL curl -4 "https://netarz.ir/api/fx/v1/rates/USD" -H "Authorization: Bearer $NETARZ_FX_KEY"
PHP cURL curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
Laravel Http::withOptions(['force_ip_resolve' => 'v4'])->withToken($key)->get($url);
Node.js import dns from "node:dns"; dns.setDefaultResultOrder("ipv4first");
Python import socket, urllib3.util.connection as c; c.allowed_gai_family = lambda: socket.AF_INET
راه دوم ثبت خود IPv6 است. curl -6 https://api64.ipify.org آن را نشان میدهد. برای IPv6 فقط آدرس دقیق پذیرفته میشود؛ پیشوند و محدوده (CIDR) فقط برای IPv4 کار میکند.
سایت پشت Cloudflare و هاست اشتراکی
اگر سایتتان پشت Cloudflare یا هر CDN دیگری است، IPی که از دامنهٔ شما دیده میشود مال Cloudflare است، نه سرور شما. درخواستی که سرور شما به نِت اَرز میفرستد، از IP خود سرور بیرون میرود؛ همان را ثبت کنید، نه IP دامنه را.
بیشتر هاستهای اشتراکی یک IP خروجی ثابت دارند که با IP سایت یکی نیست. یک بار درخواست بفرستید و IP را از فیلد ip خطا بردارید. اگر هاست آن را هر چند وقت عوض میکند، یکی از دو راه بخش بعد را انتخاب کنید. کاربران وردپرس میتوانند همین کار را با دکمهٔ «آزمایش اتصال» افزونهٔ نرخ ارز انجام بدهند که IP دیدهشده را مستقیم نشان میدهد.
Vercel، Workers و Google Sheets: وقتی IP ثابت نیست
IP خروجی Cloudflare Workers، Vercel، Netlify، Google Apps Script، n8n ابری، Zapier و Make ثابت نیست و بین هزاران مشتری همان سرویس مشترک است. محدودهٔ IP این سرویسها را ثبت نکنید؛ با این کار هر کسی روی همان سرویس هم میتواند با کلید شما درخواست بفرستد. بههمین دلیل، ما هم IP مشتری را از طرف خودمان به فهرست کسی اضافه نمیکنیم. دو راه درست دارید:
- فراخوانی از مرورگر. اگر نرخ را فقط در صفحه نشان میدهید، سرور لازم نیست. جاوااسکریپت صفحه مستقیم با کلید درخواست میدهد و قفل دامنه از کلید محافظت میکند. یا ویجت نرخ ارز را با یک تگ script بگذارید.
- یک رلهٔ کوچک روی سرور با IP ثابت. یک فایل PHP روی سروری که IP ثابت دارد، کلید را نگه میدارد، جواب را کش میکند و به بقیه JSON یا CSV میدهد. IP همان یک سرور را ثبت میکنید و بقیه از رله میخوانند.
فایل رله در مخزن netarz/fx-api-examples آماده است: relay/fx-relay.php. دو متغیر محیطی میخواهد: NETARZ_FX_KEY برای کلید اپ و FX_RELAY_TOKEN، یک رشتهٔ تصادفی دستکم ۲۴ نویسهای که فقط خودتان میدانید. رله فقط نرخ و مبدل را جواب میدهد تا به یک پروکسی باز تبدیل نشود. بعد از آن، در Google Sheets یک فرمول کافی است:
=IMPORTDATA("https://your-server.example/fx-relay.php?token=YOUR_RELAY_TOKEN&codes=USD,EUR,AED&format=csv")
همین نشانی با format=csv در Power Query اکسل هم کار میکند، و اگر اکسل روی لپتاپ اجرا میشود که IP خانه یا اینترنت همراهش مدام عوض میشود، تنها راه پایدار همین است. روشهای کامل صفحهگسترده در نرخ دلار در گوگل شیت و اکسل آمده است. برای سرور رله هم یک سرور مجازی کوچک کافی است.
اگر در «درخواستهای ردشده» هیچ چیزی نیست
گاهی خطا اصلاً ۴۰۳ نیست؛ درخواست بعد از چند ثانیه با خطای زمان (Timeout) یا «Connection refused» تمام میشود. اگر در همان لحظه در بخش «درخواستهای ردشده» پنل ردیفی ثبت نشده و مصرف امروز هم تکان نخورده، یعنی درخواست اصلاً به نِت اَرز نرسیده و ما آن را رد نکردهایم. علت را در شبکهٔ سرور خودتان جستوجو کنید: دیوارهٔ آتش (فایروال) خروجی هاست، DNS، یا مسیری که سرور شما تا ایران دارد. یک آزمون ساده این است که همان curl -4 را از سرور دیگری بفرستید؛ اگر آنجا جواب گرفتید، مشکل از سرور اول است.
چند توصیهٔ کوتاه
0.0.0.0/0یا محدودههای خیلی باز قفل را بیاثر میکنند. فهرست را تا جای ممکن باریک نگه دارید.- کلید را در نشانی (
?key=) نفرستید؛ وبسرویس فقط سربرگAuthorization: BearerیاX-API-Keyرا میپذیرد. پارامترkeyفقط برای ویجت است. - یک کش یک تا پنج دقیقهای سمت خودتان بگذارید. نرخ هر چند دقیقه عوض میشود و درخواست بیشتر فقط سهمیه میبرد.
- اگر هنوز وصل نمیشوید، از پنل تیکت بزنید و شناسهٔ اپ و متن کامل پاسخ خطا را بفرستید.
همهٔ این توضیحها، بهعلاوهٔ نمونهٔ کامل هر زبان، در راهنمای قفل دامنه و IP مخزن نمونهکدها هم آمده است. نمونهکدهای PHP، پایتون و Node.js که از روز اول روی IPv4 تنظیم شدهاند در همان مخزناند، و فهرست همهٔ مخزنهای ما در صفحهٔ کدهای متنباز نِت اَرز است. اگر هنوز اپی نساختهاید، از صفحهٔ API نرخ ارز شروع کنید و برای نمایش نرخ در صفحه، نمایش نرخ ارز زنده در سایت را ببینید.
آموزشهای کاربردی در گیسو
گیسو برند هوش مصنوعی نِت اَرز است، با همین حساب و همین اعتبار. این راهنماها قدمبهقدم نشان میدهند همین کار را چطور با هوش مصنوعی انجام دهید.
پرسشهای پرتکرار
IP سرور را ثبت کردهام ولی باز ip_not_allowed میگیرم. چرا؟
در بیشتر موارد سرور با IPv6 وصل میشود و شما IPv4 را ثبت کردهاید. درخواست را روی IPv4 نگه دارید، مثلاً با curl -4، یا همان IPی را که در فیلد ip پاسخ خطا آمده ثبت کنید.
محدودهٔ IP گوگل یا Vercel را ثبت کنم؟
نه. این IPها بین مشتریهای زیادی مشترک است و با ثبتشان، دیگران هم میتوانند با کلید شما درخواست بفرستند. از مرورگر یا ویجت استفاده کنید، یا یک رلهٔ کوچک روی سروری با IP ثابت بگذارید.
سایتم پشت Cloudflare است. کدام IP را ثبت کنم؟
IP خروجی سرور خودتان را، نه IPی که دامنه به آن اشاره میکند. آن IP مال Cloudflare است و درخواستهای سرور شما از آن بیرون نمیروند.
درخواستهای ردشده از سهمیهٔ روزانه کم میشوند؟
نه. درخواستی که بهخاطر قفل دامنه یا IP رد شود از سهمیه کم نمیشود و با زمان، علت و IP در بخش «درخواستهای ردشده» صفحهٔ اپ ثبت میشود.
نظر خوانندگان
هنوز نظری ثبت نشده. اگر این مقاله پرسشتان را جواب داد یا جای چیزی در آن خالی ماند، همینجا بنویسید.