返回博客
指南
Mihnea-Octavian ManolacheLast updated on May 12, 20263 min read

阿克西斯 2026 年的标题设置:开发者手册

阿克西斯 2026 年的标题设置:开发者手册
简而言之:Axios 在五个层级上设置头部信息,包括按请求配置、全局默认值、 axios.create() 实例、请求和响应拦截器,以及响应本身。本指南将通过可运行的 v1 代码片段逐层讲解,并修复四个让所有人都头疼的常见问题:多部分请求分界、CORS Cookie、自签名证书以及标头大小写问题。

Axios 依然是大多数 JavaScript 和 TypeScript 团队首选的 HTTP 客户端,而用户报告的大部分“Axios 错误”其实根本不是 Axios 的问题,而是头部配置问题。一个配置参数的位置错误,就会悄无声息地导致你的 Authorization 。一个手动输入的 Content-Type: multipart/form-data 会导致每次上传都失败。一个全局 axios.defaults 令牌会泄露给您调用的每个第三方主机。一旦您了解该标头在请求生命周期中的正确位置,这些问题都有明确的解决方法。

这份 Axios 设置头部指南,正是我当初搭建首个拦截器栈时梦寐以求的指南。它针对 Node 20+ 上的 Axios v1,但其中的模式同样适用于浏览器环境(尽管存在差异)。 你将看到按请求设置的头部、全局默认值、作用域实例,以及请求和响应拦截器并列呈现,并附有选择它们的决策规则。你还将获得一个完整的 Axios 响应头部章节(大多数教程都会跳过),以及针对你在生产环境中实际遇到的错误的故障排除部分。

在 Node.js 和浏览器中配置 Axios v1

在尝试以下任何模式之前,请先锁定版本以确保其行为与文档描述一致。本文针对 Node 20 或更高版本上的 Axios v1.x。0.x 系列对头部字段的规范化处理方式不同,因此 v1 的模式无法完全无缝迁移。

通过任意包管理器安装:

npm install axios
# or: yarn add axios   |   pnpm add axios

ESM 项目:

import axios from "axios";

CommonJS 服务:

const axios = require("axios");

普通浏览器页面:

<script src="https://cdn.jsdelivr.net/npm/axios@1/dist/axios.min.js"></script>

一个稍后会再次提及的环境细节:在 Node 中,Axios 使用内置的 http/https 堆栈,因此您可以控制 TLS、代理和代理服务器。而在浏览器中,它使用 XMLHttpRequest,因此浏览器会强制执行 CORS 及 SameSite 规则,无论你的配置如何。如果你还需要代理层,一份专门的 Axios 代理指南将详细介绍端到端的配置流程。

通过配置对象按请求传递头部

按请求设置头部是 Axios 为单次调用设置头部最直接的方式。对于一次性认证、内容协商和隔离调试,这是正确的默认选择。不同调用之间不会共享任何内容,也不会泄露到其他模块中,且通过这种方式设置的头部优先于其下方的所有默认层。

// GET with auth and a custom Accept
const orders = await axios.get("https://api.example.com/orders", {
  headers: {
    Authorization: `Bearer ${token}`,
    Accept: "application/vnd.example.v2+json",
  },
});

// POST: body in arg 2, config in arg 3
const created = await axios.post(
  "https://api.example.com/orders",
  { sku: "ABC-123", quantity: 2 },
  { headers: { "Idempotency-Key": crypto.randomUUID() } }
);

// PUT and PATCH follow the same three-argument shape as POST
await axios.put(url, body, { headers: { "If-Match": etag } });
await axios.patch(url, body, {
  headers: { "Content-Type": "application/merge-patch+json" },
});

// DELETE takes config in arg 2, like GET
await axios.delete(url, {
  headers: { Authorization: `Bearer ${token}` },
});

有几条规则值得牢记。在 v1 中,headers 对象仅接受字符串值;数组和 null 不被支持。您为每次请求设置的值具有最终决定权,即使请求拦截器随后试图覆盖它(下文将展示 guard 模式)。而这个 Axios 头部示例是特定请求忽略所有全局设置的最简洁方式,因为按请求设置的键值会优先于其他所有层。

参数槽陷阱:GET/DELETE 与 POST/PUT/PATCH

初学者提交的 Axios 错误报告中最常见的问题根本不是头部相关,而是参数槽位问题。 axios.get 以及 axios.delete 接受配置作为第二个参数,因为它们没有请求主体。 axios.post, axios.put,以及 axios.patch 将配置作为第三个参数接受,因为请求体位于第二个参数中。

如果搞错了,你的头部信息就会被放入正文中。服务器看不到 Authorization,返回 401 状态码,而你却要花上一小时去排查令牌存储的问题。

// WRONG: headers object treated as the POST body
await axios.post("https://api.example.com/orders", {
  headers: { Authorization: `Bearer ${token}` }, // becomes the request body
});

// RIGHT: empty body, headers in the third slot
await axios.post(
  "https://api.example.com/orders",
  {},                                                // body
  { headers: { Authorization: `Bearer ${token}` } }  // config
);

确认 401 错误是否由此位置错误引起的最快方法是记录 error.config.data 并检查头部是否泄露到了正文中。如果泄露了,就把它们向右移一个位置。这是 Axios 头部 POST 请求中最典型的错误,一旦你掌握了位置规则,它就会消失。

为每个请求设置全局默认值

Axios的全局头部是在 axios.defaults.headers ,并适用于通过默认实例发出的所有调用。它们仅适用于特定场景:仅与单个 API 交互的脚本、内部开发工具、命令行工具(CLI),或是自带 API 客户端的小型应用。一旦你需要调用多个主机,默认设置就会变成负担。

// Apply to every method
axios.defaults.headers.common["Accept"] = "application/json";
axios.defaults.headers.common["X-App-Version"] = "2026.04.1";

// Pull secrets from env, never inline
axios.defaults.baseURL = process.env.API_URL;
axios.defaults.headers.common["Authorization"] =
  `Bearer ${process.env.API_TOKEN}`;

common 桶(bucket)通常是您想要的,因为它不受 HTTP 方法限制。您在此处设置的任何内容都会出现在默认 Axios 单例发出的每个外发 GET、POST、PUT、PATCH、DELETE 和 OPTIONS 请求中。

维护成本会在后续显现。一旦第二个服务或第三方 SDK 存在于同一代码库中,你的全局 Authorization 头部就会被发送到它本不该触及的主机。下文的实例部分将妥善解决这一问题。

特定于方法的默认值(post、get、put)

Axios 还提供了方法级别的配置项,例如 axios.defaults.headers.post, .get, .put, .patch.delete。这些仅适用于特定动词,当你希望为每种方法设置不同的默认值时,这偶尔会派上用场。

// Force a JSON content type only on writes
axios.defaults.headers.post["Content-Type"] = "application/json";
axios.defaults.headers.put["Content-Type"] = "application/json";
axios.defaults.headers.patch["Content-Type"] = "application/json";

对于其他所有情况,请优先使用 common ,仅在确有必要时才限定为特定方法的桶。实际上,对于大多数团队而言,方法桶仅作为参考知识存在。实例几乎总是更简洁的实现方式,而下一节将说明 Axios 的默认头部何时不再是你的主要工具。

使用 axios.create() 实例为每个 API 隔离头部集

axios.create() 是 2026 年的默认建议。你为每个上游服务构建一个小型客户端,设置其专属的 baseURL 和头部,并将该客户端分发给需要它的模块。每个实例都是独立的,因此更新一个不会影响另一个。

// Internal API: short tokens, custom version header
export const internalApi = axios.create({
  baseURL: "https://internal.example.com/api",
  headers: {
    "Accept": "application/json",
    "X-Internal-Client": "checkout-svc",
  },
});

// Third-party API: completely separate auth and content negotiation
export const partnerApi = axios.create({
  baseURL: "https://partner.example.com",
  headers: {
    "Accept": "application/vnd.partner.v3+json",
    "X-Partner-Key": process.env.PARTNER_KEY,
  },
});

设置实例级头部的方式与设置全局变量相同,只是操作对象是实例本身:

// After login, write the new token to one instance only
internalApi.defaults.headers.common["Authorization"] = `Bearer ${jwt}`;

这种模式能带来两大好处。您的 Authorization 请求头只会发送给签发令牌的主机。当您替换一个上游服务时,只需修改一个客户端文件,其他部分不受影响。axios.create 配合请求头的这种用法,正是能够实现可扩展性且避免自相矛盾的解决方案。

为何共享的默认 Authorization 值存在安全风险

当你设置 axios.defaults.headers.common.Authorization = "Bearer ..."时,所有通过默认 Axios 单例发出的请求都会发送该令牌,包括导入裸 axios 模块的临时脚本。这包括那些根本不知道你的 JWT 是什么、甚至可能将其记录下来的主机。

Axios 的授权头部应始终限定在签发它的主机范围内。两种可靠的模式:

// 1. Use a dedicated instance and never touch axios.defaults
internalApi.defaults.headers.common.Authorization = `Bearer ${jwt}`;

// 2. Or guard a request interceptor by baseURL / hostname
axios.interceptors.request.use((cfg) => {
  const host = new URL(cfg.baseURL || cfg.url, "http://x").hostname;
  if (host === "internal.example.com") {
    cfg.headers.Authorization = `Bearer ${jwt}`;
  }
  return cfg;
});

对于共享单个 axios。而在其他场景下,实例模式更为简洁。

使用请求拦截器自动注入令牌

请求拦截器是 Axios 的设置标头工具,适用于需要在调用时(而非模块加载时)计算标头的情况。它会在每个出站请求离开 Axios 之前运行。这正是 Axios 请求拦截器模式大显身手之处:从存储位置读取当前令牌,将其附加到 config.headers,并返回修改后的配置。

两种存储方式足以满足大多数应用的需求。在浏览器中,通常从 localStorage, sessionStorage或内存中读取。在 Node 环境中,则维护一个小型令牌存储模块,供应用程序的其他部分调用。

// Browser: read the latest token on every request
internalApi.interceptors.request.use((config) => {
  const token = localStorage.getItem("access_token");
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});
// Node: module-level store, swap-able for Redis or a vault
import { getToken } from "./auth/token-store.js";

internalApi.interceptors.request.use(async (config) => {
  const token = await getToken();
  if (token && !config.headers.Authorization) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

那个 if (!config.headers.Authorization) 保护机制至关重要。这也是当您需要时,允许按请求覆盖优先于拦截器的模式,合并顺序部分将对此进行详细说明。

拦截器还处理那些基于请求和默认层无法清晰表达的条件逻辑。基于角色的标头、环境切换以及基于 URL 的行为都属于此范畴:

internalApi.interceptors.request.use((config) => {
  const user = getCurrentUser();
  if (user?.role === "admin") {
    config.headers["X-Admin"] = "true";
  }
  if (config.url?.startsWith("/billing/")) {
    config.headers["X-Sensitive-Path"] = "1";
  }
  if (process.env.NODE_ENV === "development") {
    config.headers["X-Debug-Trace"] = crypto.randomUUID();
  }
  return config;
});

尽可能为每个关注点保留一个拦截器。将身份验证、角色标记和可观测性混入一个函数会使调试变得痛苦,而且每个已注册的拦截器无论如何都只按注册顺序运行。

使用响应拦截器在 401 状态下刷新过期令牌

令牌刷新是大多数 Axios 代码库容易出现隐蔽 bug 的地方:无限重试循环、并行请求间的竞争条件,以及本应负责刷新令牌的拦截器反而重写了刷新请求。下面的模式可以避免这三种情况。

该方案采用响应拦截器,其逻辑为:捕获 401 状态码,通过独立的 Axios 客户端调用刷新端点,标记原始配置以防止无限重试,进行一次重试,并在失败时交由注销流程处理。

import axios from "axios";

// 1. Plain client for refresh, with NO interceptors of its own.
//    This is what keeps the refresh call out of the retry loop.
const authClient = axios.create({ baseURL: "https://auth.example.com" });

// 2. Your normal API client with auth wiring.
const api = axios.create({ baseURL: "https://api.example.com" });

api.interceptors.request.use((cfg) => {
  const t = tokenStore.access;
  if (t) cfg.headers.Authorization = `Bearer ${t}`;
  return cfg;
});

api.interceptors.response.use(
  (res) => res,
  async (error) => {
    const original = error.config;
    if (
      error.response?.status !== 401 ||
      original._retry ||
      !tokenStore.refresh
    ) {
      // Either not an auth failure, already retried once,
      // or we have nothing to refresh with.
      return Promise.reject(error);
    }
    original._retry = true; // 3. one shot only, no loops
    try {
      const { data } = await authClient.post("/refresh", {
        refresh_token: tokenStore.refresh,
      });
      tokenStore.access = data.access;
      tokenStore.refresh = data.refresh;
      // 4. write the new token onto the original request and replay it
      original.headers.Authorization = `Bearer ${data.access}`;
      return api(original);
    } catch (refreshErr) {
      tokenStore.clear();
      window.location.href = "/login"; // or your auth flow
      return Promise.reject(refreshErr);
    }
  }
);

在正式上线前有几点生产环境注意事项。请在标签页之间共享令牌存储( BroadcastChannel 监听器在浏览器中有效),确保一个标签页的刷新操作不会导致另一个标签页陷入僵局。将正在进行的刷新操作封装在 Promise 单例中,确保十个并行 401 错误仅触发一次刷新,而非十次。并且将重试次数限制在 _retry ,除非刷新端点本身可能返回 401 状态码,否则会因会话已失效而陷入无限循环。

解析响应头:速率限制、ETag 和分页

大多数关于设置 Axios 请求头的指南仅关注请求端,却从未关注响应内容。这是个错误。Axios 的响应头对象正是 API 告知你后续行为的关键:剩余请求次数、缓存副本是否仍有效,以及下一页的位置。若忽略这些信息,你的爬虫在第一天看似礼貌,但到第三天就会被限流。

Axios 将响应头暴露在 response.headers上。无论服务器在传输过程中如何处理大小写,这些键值均以小写形式呈现。这是初次阅读时最常见的意外情况。MDN 的《HTTP 条件请求指南涵盖了 ETag 的规范, If-None-Match.

const res = await api.get("/orders");

// All read as lowercase keys, regardless of server casing
const limit = Number(res.headers["x-ratelimit-limit"]);
const remaining = Number(res.headers["x-ratelimit-remaining"]);
const resetAt = new Date(Number(res.headers["x-ratelimit-reset"]) * 1000);

console.log(`API budget: ${remaining}/${limit}, resets at ${resetAt.toISOString()}`);

为了实现缓存友好的轮询,请将 ETag ,并通过 If-None-Match。如果资源未发生变化,服务器将返回 304 状态码并附带空正文,你的代码将跳过解析:

let cachedEtag = null;
let cachedBody = null;

async function pollOrders() {
  try {
    const res = await api.get("/orders", {
      headers: cachedEtag ? { "If-None-Match": cachedEtag } : {},
      validateStatus: (s) => s === 200 || s === 304, // 304 is not an error
    });
    if (res.status === 200) {
      cachedEtag = res.headers["etag"];
      cachedBody = res.data;
    }
    return cachedBody;
  } catch (err) {
    /* network/HTTP errors only land here now */
  }
}

分页 API 通常使用 Link 标头(RFC 8288)配合 rel="next"rel="prev"。可手动遍历或使用解析器:

function nextLink(linkHeader) {
  if (!linkHeader) return null;
  const match = linkHeader.split(",").find((p) => /rel="next"/.test(p));
  return match ? match.match(/<([^>]+)>/)[1] : null;
}

let url = "/orders?per_page=100";
while (url) {
  const res = await api.get(url);
  process(res.data);
  url = nextLink(res.headers["link"]);
}

大多数 API 中值得监控的头部字段:

标头

其含义

x-ratelimit-remaining, x-ratelimit-reset

距离 429 状态码还有多远,以及资源池何时恢复

retry-after

收到 429 或 503 错误后需等待的秒数或 HTTP 时间

etag, last-modified

条件 GET 请求的缓存验证器

link

符合 RFC 8288 标准的分页游标

content-type

请求主体的格式是 JSON、XML、NDJSON 还是 HTML

如果您的代码仅检查 response.data,你就忽略了 API 试图传达给你的半数信息。

Axios 如何合并头部:2026 年的优先级规则

每当你通过 Axios 从多个层设置头部时,该库都必须合并它们。头部是在请求时通过遍历五层并应用“最后写入者胜出”原则来计算的。了解顺序才能让你预测服务器实际看到的是哪个头部。

合并顺序(从优先级最低到最高):

  1. 库默认值。Axios 内置值,如 Accept: application/json, text/plain, */* 以及针对写入请求的 Content-Type (用于写入请求)。
  2. 全局默认值。您在 axios.defaults.headers.common, .post, .get及其相关对象上设置的任何内容。
  3. 实例默认值。在通过 axios.create({ headers: ... }) 或随后分配给 instance.defaults.headers.* 的客户端上设置的标头。
  4. 按请求配置。 headers 直接传递给 axios.get(url, { headers }), axios.post(url, body, { headers })的对象,等等。
  5. 请求拦截器。拦截器写入 config.headers 的内容,这些操作发生在请求离开 Axios 之前。这些操作在按请求合并之后执行。

由于拦截器最后执行,从技术上讲它们会覆盖请求级头部。这几乎会让所有人第一次使用时感到困惑。解决方法是在拦截器内部添加一个检查现有值的条件:

api.interceptors.request.use((config) => {
  // Only inject if the caller has not already set Authorization
  if (!config.headers.Authorization) {
    const t = tokenStore.access;
    if (t) config.headers.Authorization = `Bearer ${t}`;
  }
  return config;
});

有了这个条件判断,请求级别的 Authorization 就能在拦截器栈中保留下来,你的覆盖逻辑也会按直觉运行。这与允许某个特定调用使用服务间令牌,而应用其余部分使用用户令牌的模式是一样的。

修复最常见的 Axios 头部错误

大多数生产环境中的头部问题可归纳为四种模式:上传时多部分边界被剥离、浏览器中的 CORS 或 withCredentials 不匹配、Node 中的自签名 TLS 证书,以及因区分大小写而拒绝 Axios 发送的网络格式的后端。下文的四个子章节均经过精心设计,以便您能通过搜索直接跳转至任意章节,并无需滚动页面即可应用修复方案。

多部分/表单数据上传失败(分界符陷阱)

Axios FormData Content-Type 错误中最常见的一个,正是人们凭直觉采取的“修复”措施:将 Content-Type: multipart/form-data 。仅此值是不完整的,因为真正的 multipart 主体需要 boundary 参数,供服务器用于分隔字段。若硬编码该裸值,Axios 不会覆盖它,实际分隔符将无法到达网络传输层,导致服务器拒绝上传。

正确的做法是让运行时为你自动生成该头部。

// Node: form-data library exposes getHeaders() with the boundary baked in
import FormData from "form-data";
import fs from "node:fs";

const form = new FormData();
form.append("file", fs.createReadStream("./invoice.pdf"));
form.append("note", "Q4 invoice");

await api.post("/uploads", form, {
  headers: form.getHeaders(), // multipart/form-data; boundary=...
});
// Browser: send a real FormData and let Axios omit Content-Type
const fd = new FormData();
fd.append("file", fileInput.files[0]);
fd.append("note", "Q4 invoice");

await api.post("/uploads", fd); // do not set Content-Type

唯一例外:如果你向 Axios 传递一个普通对象并让它在内部序列化为 FormData,显式设置 Content-Type: multipart/form-data 是可行的,因为 Axios 会自行填充分隔符。

CORS 仅存在于浏览器中。Node 没有 CORS、没有被阻止的头部,也没有预检,因此本节内容仅适用于在浏览器标签页中进行调试。无法从客户端禁用 Axios 的 CORS 头部;规则由浏览器掌控,而 Axios 处于其下游。关于浏览器强制执行规则的权威参考是 MDN CORS 指南

要让跨源 Cookie 正常传输,必须满足以下四个条件:

  1. 客户端需在 Axios 中启用 withCredentials 选项。即 instance.defaults.withCredentials = true,或 { withCredentials: true } 按请求设置。
  2. 服务器返回 Access-Control-Allow-Credentials: true.
  3. Access-Control-Allow-Origin 响应中包含特定源,绝不 *。一旦涉及凭据,通配符将被拒绝。
  4. Cookie 会带有 SameSite=None; Secure。一个 SameSite=Strict Cookie 绝不会在跨站点时发送,即使 withCredentials 已设置。
const api = axios.create({
  baseURL: "https://api.example.com",
  withCredentials: true, // tells the BROWSER to attach cookies
});

如果您不拥有上游 API 且无法修复 CORS 响应,唯一的妥善解决方案是在您自己的域名上部署一个小型服务器端代理来转发请求。将该代理部署在您自己的子域名下,还能规避第三方 Cookie 被弃用的问题。关于 Cookie 底层行为的入门指南值得收藏。

Node.js 中的自签名 HTTPS 证书

在 Node 中,您可以完全控制 TLS,因此可以与使用自签名证书的开发 API 通信,而无需向运行时环境发出警告。但在浏览器中,您无法做到这一点,也不应尝试;用户必须信任操作系统或浏览器存储中的 CA。

仅限 Node 端开发或预发布环境,请配置自定义 HTTPS 代理。Node.js https.Agent 文档涵盖了所有选项:

import https from "node:https";

const devApi = axios.create({
  baseURL: "https://dev.internal.local",
  httpsAgent: new https.Agent({ rejectUnauthorized: false }),
});

该标志会禁用证书验证,这意味着中间人攻击将无法被检测到。切勿将此配置部署到生产环境。生产环境的解决方案是将您的 CA 添加到系统受信任存储中,或者签发真实证书(例如 Let's Encrypt、内部 ACME 服务器或云服务提供商的证书管理器)。 rejectUnauthorized: false 这只是开发迭代中的应急工具,而非部署策略。

挑剔的旧版后端对标头大小写的要求

理论上 HTTP 不区分大小写,但相当数量的旧版后端会拒绝那些标头名称不符合规范混合大小写格式的请求。Axios 会在内部对某些标头名称进行标准化处理,但对于自定义标头,您应发送上游服务器所期望的大小写格式。

// Safer than x-api-key on older middleware
await api.get("/secret", {
  headers: { "X-API-Key": process.env.API_KEY },
});

反之:当你从 response.headers,Axios 会将所有键转换为小写。因此,您发送 X-API-Key 但读取 res.headers["x-api-key"]。这种非对称性是刻意设计的,且在 v1 版本中保持一致。对外发送时采用规范大小写,接收时接受小写,您将再也不会遇到“缺少头部”的调试问题。

使用 Axios 进行网页抓取时发送类似浏览器的头部

Web 服务器会检查请求头来判断客户端是真实的浏览器还是机器人,而默认的 Node 用户代理如 axios/1.7.7 是最容易被标记的方式。若使用 Axios 进行抓取,请发送可信的头部组合并定期轮换。

const UA_POOL = [
  "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
  "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Safari/605.1.15",
  "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
];

const scraper = axios.create({
  headers: {
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
    "Accept-Language": "en-US,en;q=0.9",
    "Accept-Encoding": "gzip, deflate, br",
    "Upgrade-Insecure-Requests": "1",
  },
  timeout: 15_000,
});

// Axios User-Agent rotation per request
scraper.interceptors.request.use((cfg) => {
  cfg.headers["User-Agent"] = UA_POOL[Math.floor(Math.random() * UA_POOL.length)];
  return cfg;
});

这能让你绕过简单的机器人过滤器。但复杂的目标会叠加JA3指纹、TLS配置文件、Cloudflare验证以及行为检查等多重防护,仅靠头信息包无法通过这些检测。当 axios 加上轮换策略仍不足以应对时,请升级至住宅代理池或托管式爬取 API,它们能为你处理指纹识别、重试及 CAPTCHA 验证工作流。关于爬虫为何在头部信息之外仍会被封锁的详细分析,是值得进一步阅读的内容。

2026年Axios设置头部参数的关键要点

整个 Axios 设置头部模型可归纳为一个五层思维模型,外加一条排序规则:

  • 针对一次性请求和覆盖设置的按请求配置;优先于下方所有默认值。
  • 全局默认值仅适用于单API脚本和开发工具;一旦调用多个主机,风险便随之而来。
  • axios.create() 对于与多个上游通信的任何应用,实例配置应作为2026年的默认方案。
  • 请求拦截器用于处理横切关注点,如身份验证注入、角色标记和追踪。
  • 响应检查,以便 response.headers 实际影响您的重试、缓存和分页逻辑。

合并顺序,从低到高:库默认值、全局默认值、实例默认值、按请求配置、请求拦截器。当您希望按请求保持其优先级时,请在拦截器内部使用 if (!config.headers.x) guard

常见问题

axios.defaults.headers.common 和 axios.defaults.headers.post 之间有什么区别?

common 适用于默认 Axios 实例上的每个 HTTP 方法,因此在此处设置的标头将随 GET、POST、PUT、PATCH 和 DELETE 一起发送。 post (而 .get, .put, .patch, .delete) 仅在使用该特定动词时才生效。请优先使用 common ;仅当某个动词确实需要不同的默认值时,才使用方法分类。

如何让 Axios 中的按请求设置的标头优先于请求拦截器?

对拦截器进行条件判断,确保仅在调用方尚未设置头部时才写入头部。模式如下 if (!config.headers.Authorization) { config.headers.Authorization = ... }。由于拦截器在请求级合并之后运行,无条件赋值会覆盖你的重写。带条件检查的版本会尊重调用方传递的任何值。

为什么从 response.headers 读取时,Axios 会将标头名称转换为小写?

根据惯例,Axios 会将响应头键规范化为小写。HTTP 本身对头名称不区分大小写,而在读取路径上将其转为小写意味着你永远不必猜测 etag, content-type,或 x-ratelimit-remaining。发送时采用规范大小写,接收时读取小写。

从浏览器调用第三方 API 时,Axios 能否绕过 CORS 限制?

不能。CORS 由浏览器在 Axios 运行之前强制执行,因此不存在客户端标志可以禁用它。唯一的解决方法是让上游服务器发送正确的 Access-Control-Allow-OriginAccess-Control-Allow-Credentials ,或者通过您控制的小型服务器端代理进行路由。Node 完全没有 CORS 规则。

如何仅将 Bearer 令牌发送至特定的 baseURL,同时避免将其泄露给其他主机?

为该 API 创建一个专用的 axios.create() 实例,并将 Authorization 仅在该实例上。切勿将令牌写入 axios.defaults,否则默认单例会将其广播给所有调用的主机。若必须使用一个共享实例,请在请求拦截器中添加主机名校验,确保仅当 URL 与可信 baseURL 匹配时才附加该标头。

总结

若您从这篇 Axios 设置头部指南中只能记住一件事,那就是分层原则:每个头部都位于五个位置之一,顺序依次为库默认值、全局默认值、实例默认值、按请求配置,最后是请求拦截器。选择与头部生命周期最匹配的最高层级。一次性覆盖应放在按请求配置中。全应用范围的配置应放在实例中。 横切关注的身份验证应放在拦截器中。而且每个 Axios 响应都携带了应影响你后续决策的头部,而非被丢弃。

当你处理的不仅仅是行为规范的 REST API 时,同样的模式依然适用。一旦你将 Axios 指向会采集客户端指纹、实施严格速率限制,或通过 JavaScript 验证隐藏数据的目标,仅靠标头轮换就不再足够。 此时,采用托管层才是更具杠杆效应的策略:WebScrapingAPI 的 Scraper API 通过单一端点处理代理轮换、浏览器级 TLS 指纹识别以及 CAPTCHA 验证,因此你刚编写的 Axios 代码保持原样,你也不再需要忙于处理阻塞问题。将此页面收藏为你的 Axios 设置头部参考,当请求层不再是核心问题时,请选择托管方案。

关于作者
Mihnea-Octavian Manolache, 全栈开发工程师 @ WebScrapingAPI
Mihnea-Octavian Manolache全栈开发工程师

Mihnea-Octavian Manolache 是 WebScrapingAPI 的全栈及 DevOps 工程师,负责开发产品功能并维护确保平台平稳运行的基础设施。

开始构建

准备好扩展您的数据收集规模了吗?

加入2,000多家企业,使用WebScrapingAPI在无需任何基础设施开销的情况下,以企业级规模提取网络数据。