PAYATHON 2026

axios.get() 调用 API 时为何返回 403 错误?

技术老齐

结论

这不是 Axios 故障,也与 CORS 无关。响应头里的 server: cloudflare、cf-ray 和 content-type: text/html 说明,请求被 Cloudflare 或目标站点的防护规则拦截了。

浏览器能够正常访问,是因为它会携带浏览器指纹、Cookie,还可能已经通过 Cloudflare 验证。Node.js 中的 Axios 请求通常只有 User-Agent: axios/0.24.0 等少量请求头,更容易被识别为脚本流量,随后收到 403 Forbidden。

isAxiosError: true 只说明 Axios 把这次失败封装成了 Axios 错误,不代表拒绝请求的是 Axios。

为什么浏览器和 Axios 的结果不同

即使 URL 完全一样,浏览器和 Axios 发出的请求也不相同,差异可能包括:

  • User-Agent、Accept、Accept-Language 等请求头。
  • 浏览器可能带有登录状态、会话 Cookie 或 Cloudflare 验证 Cookie。
  • 浏览器可以执行 JavaScript 验证,Axios 不行。
  • 服务端可能根据 IP、TLS 指纹、请求频率或数据中心网络评估请求风险。
  • 该接口可能只供网站内部调用,并未向任意服务器端程序开放。
  • 接口可能要求 API Key、签名或其他认证信息。

错误响应的正文类型是 text/html,不是预期的 JSON。这通常意味着返回的是安全验证页面或拦截页面。

请求中的这一项也应该删除:

Access-Control-Allow-Origin: *

Access-Control-Allow-Origin 是由服务器写入响应的 CORS 响应头,不是客户端用来申请跨域权限的请求头。Node.js 请求不受浏览器同源策略限制,所以添加它无法解决 403,反而可能让防护系统将请求判断为异常。

建议的解决顺序

1. 优先使用官方公开 API

先确认 Magic Eden 当前是否为该接口提供正式的开发者入口,并查看是否需要以下配置:

  • API Key
  • Authorization 请求头
  • 请求签名
  • 指定的 API 域名
  • 调用频率限制
  • IP 白名单

示例:

const axios = require('axios');

const response = await axios.get('https://example.com/official-api/endpoint', {
  params: {
    // 按官方文档填写参数
  },
  headers: {
    Accept: 'application/json',
    Authorization: `Bearer ${process.env.MAGIC_EDEN_API_TOKEN}`
  },
  timeout: 10000
});

console.log(response.data);

不要把密钥直接写进源码,也不要将其提交到代码仓库。

2. 使用 params 生成查询字符串

当前 URL 将 JSON 查询条件手工编码进了字符串。虽然日志显示 Axios 最终完成了编码,但改用 params 更便于维护,也能减少重复编码或编码不一致:

const axios = require('axios');

const query = {
  $match: {
    txType: 'initializeEscrow',
    blockTime: {
      $gt: 1643376700
    }
  },
  $sort: {
    blockTime: -1
  }
};

async function getActivities() {
  try {
    const response = await axios.get(
      'https://api-mainnet.magiceden.io/rpc/getGlobalActivitiesByQuery',
      {
        params: {
          q: JSON.stringify(query)
        },
        headers: {
          Accept: 'application/json'
        },
        timeout: 10000
      }
    );

    console.log(response.data);
  } catch (error) {
    if (axios.isAxiosError(error)) {
      console.error('status:', error.response?.status);
      console.error('content-type:', error.response?.headers?.['content-type']);
      console.error('response:', error.response?.data);
    } else {
      console.error(error);
    }
  }
}

getActivities();

这样可以确保参数正确生成。不过,如果服务端明确拦截自动化请求,只修改编码方式仍然无法消除 403。

3. 对比浏览器和 Node.js 请求

在浏览器开发者工具的 Network 面板中找到成功的请求,再与 Axios 请求逐项比较:

  • 实际请求 URL
  • 是否发生重定向
  • Authorization
  • Cookie
  • Origin 和 Referer
  • 请求方法
  • 查询参数编码
  • 响应正文
  • 调用频率

还可以把成功的请求复制为 cURL,然后在运行 Node.js 的同一台机器上执行。如果 cURL 也返回 403,问题通常与服务器 IP、防护策略或认证状态有关。如果 cURL 成功而 Axios 失败,再逐项检查两者的请求差异。

4. 仅在服务条款允许时补充普通请求头

有些站点会拒绝明显缺少常规请求头的请求。可以尝试提供合理的 User-Agent 和语言信息:

const response = await axios.get(url, {
  headers: {
    Accept: 'application/json, text/plain, */*',
    'Accept-Language': 'en-US,en;q=0.9',
    'User-Agent': 'MySolanaTracker/1.0 (contact: admin@example.com)'
  }
});

这种做法只适用于服务端允许程序调用,但要求客户端清楚标识自身的情况。伪造完整的浏览器请求头也无法保证通过 Cloudflare,因为防护判断还可能涉及 Cookie、JavaScript challenge、TLS 指纹和 IP 声誉。

5. 检查限流和出口 IP

如果接口以前能够调用,后来才开始返回 403,还需要检查:

  • 是否在短时间内发送了大量请求。
  • 是否使用了云服务器或代理出口 IP。
  • 同一个 IP 是否被其他程序共享。
  • 是否存在区域限制。
  • 是否违反接口的调用频率或使用规则。

遇到限流时,应降低并发、增加指数退避,并遵守服务方公布的限制。不要通过轮换代理等方式规避访问控制。

注意事项

  • 403 表示服务器主动拒绝访问,不是 Axios 无法解析响应。
  • CORS 只约束浏览器中的前端 JavaScript,不约束 Node.js,因此设置 Access-Control-Allow-Origin 解决不了这个问题。
  • 不要直接把浏览器中的会话 Cookie 复制到生产程序。这类 Cookie 通常会过期,也可能包含账号凭据。
  • 如果 Cloudflare challenge 需要浏览器能力,Axios 无法单独完成验证。
  • 不建议依赖网站内部的 /rpc/... 接口。未公开接口可能随时修改、增加认证要求或停止对外服务。
  • 如果服务方没有提供公开的机器调用方式,可靠的处理办法是申请官方 API 权限或联系服务方,而不是继续模拟浏览器。

备注:内容仅供参考。