跳至主要内容

浏览器的OPTIONS请求

 浏览器发出多余的 OPTIONS 请求,通常与跨域请求(CORS)和某些特定的请求条件有关。以下是一些常见导致浏览器发出“额外” OPTIONS 请求的情况:


🧠 1. 跨域请求 + 非简单请求

当你从网页发起跨域请求(即请求的域、端口或协议与页面不同),并且该请求不是“简单请求”时,浏览器会先发出 预检请求(preflight),即 OPTIONS 请求。

非简单请求的特征:

  • 使用的方法不是 GETHEADPOST

  • 或者 POST 请求中 Content-Type 不属于下列之一:

    • application/x-www-form-urlencoded

    • multipart/form-data

    • text/plain

  • 或者设置了自定义的请求头(如 Authorization, X-Token 等)。

📌 示例:

fetch("https://api.example.com/data", {
  method: "PUT", // 非简单方法
  headers: {
    "Content-Type": "application/json" // 非简单 Content-Type
  },
  body: JSON.stringify({ key: "value" })
});

这会触发一个 OPTIONS 请求作为预检。


🧠 2. 自定义请求头

即便是 GETPOST,只要你添加了非标准头部字段,也会触发 OPTIONS 请求。

例如添加了 Authorization

fetch("/api", {
  headers: {
    "Authorization": "Bearer token"
  }
});

🧠 3. 不同的协议、端口或子域

即便你访问的是同一个主域名,但只要协议或端口不同,也会被视为跨域。

示例:

  • https://example.comhttp://example.com

  • https://example.comhttps://api.example.com

  • https://example.comhttps://example.com:8443


🧠 4. 某些浏览器或框架行为

一些 JavaScript 库或浏览器插件可能自动添加 header,导致触发预检。例如:

  • Axios 默认加了 X-Requested-With

  • 某些安全策略插件;

  • DevTools 中模拟移动设备。


🧠 5. 缓存未命中

如果服务器未设置 Access-Control-Max-Age 或时间太短,则每次跨域请求都必须重新预检,从而产生重复的 OPTIONS


✅ 减少 OPTIONS 请求的方法

  • 尽量使用“简单请求”:如 GETPOST(使用 application/x-www-form-urlencoded);

  • 避免自定义请求头(或将其放在 body 中);

  • 使用同源策略;

  • 设置合理的 Access-Control-Max-Age 响应头缓存预检结果。


这些 OPTIONS 请求最主要的意义,是出于 安全性考虑,尤其是在 跨域资源共享(CORS) 机制下,用于浏览器与服务器之间的一种 “探测”或“预检”机制


✅ OPTIONS 请求的核心意义

1. 预检请求(Preflight)——浏览器在正式请求前的安全确认

当网页 JavaScript 代码要发起一个“非简单”的跨域请求时,浏览器为了保护用户安全,不会立刻发送这个请求,而是先自动发一个 OPTIONS 请求到服务器,来询问:

“我要用这个方法(如 PUT),并带这些头(如 Authorization),你允许我这么做吗?”

服务器的责任:

  • 通过返回的 Access-Control-Allow-* 头告诉浏览器:

    • 是否允许这个源(Origin)访问;

    • 允许的方法(如 GET, POST, PUT);

    • 允许的请求头字段;

    • 是否允许携带 cookie;

    • 预检缓存时长(Access-Control-Max-Age);


2. 避免跨站攻击(CSRF、XSS)对敏感接口的滥用

举例来说,假如没有预检机制:

  • 一个恶意网页就可以随意给用户的银行后台接口发 PUT /transfer 请求,伪造转账;

  • 而浏览器默认会带上用户当前的 Cookie/session,这会导致严重的账户劫持问题。

📌 通过 OPTIONS 预检,服务器可以拒绝这些未经授权的跨源请求,从而保护用户。


3. 通用性用途(不局限于 CORS)

OPTIONS 方法本身是 HTTP 协议的一部分,含义是:

“告诉我这个 URL 支持哪些 HTTP 方法”

在一些 RESTful API 或服务器调试场景下,可以手动发送 OPTIONS 请求来获知资源支持的操作:

OPTIONS /api/user HTTP/1.1
Host: example.com

服务器返回:

Allow: GET, POST, PUT, DELETE

🧾 总结:OPTIONS 请求 ≠ 多余请求

情况 是否必须 目的
跨域 + 非简单请求 ✅ 是 浏览器的安全机制,用于询问服务端是否允许
自定义 Header ✅ 是 浏览器无法预知服务器是否支持这些 Header
RESTful 手动调用 ❌ 可选 用户主动探测 API 能力


此博客中的热门博文

Elasticsearch 读写原理指南

### 1. 什么是 segment,里面装了什么? 在 Lucene(也是 Elasticsearch)里,索引被切分成若干 **segment(段)**,每个 segment 是一个完整的、只读的倒排索引单元。一个 segment 包含: * **倒排词典** —— 用 **FST(Finite‑State Transducer)** 以高度压缩的形式保存每个字段出现的所有 term 以及 term→ord 的映射。对应的磁盘文件是 `*.tim`(新版)或 `*.tis/*.tii`(旧版)。 * **倒排列表(postings)** —— 保存每个 term 出现的文档 ID、频次、位置信息等,文件名通常是 `*.doc`、`*.pos`、`*.pay`。 * **存储字段**(_source、store:true 的字段)—— 以二进制块的形式写入 `*.fdt` / `*.fdx`。 * **doc‑values、norms、向量** 等辅助结构,分别保存在 `*.dv`、`*.norm`、`*.tv` 等文件里。 * **deleted‑docs bitmap**(`*.del`),标记哪些文档已被删除或被更新。 所有这些文件在 segment **写入磁盘后即成为只读**,后续的查询只能读取,永远不会在原文件上进行增删改。 --- ### 2. 原始文档和 FST 为什么都在 segment 里? * **原始文档**:Elasticsearch 默认把完整的 JSON(_source)以及任何 `store:true` 的字段写入 segment 的 `*.fdt/*.fdx` 文件。每个 segment 保存自己的那部分文档,旧的 segment 在合并前仍然保留,直到合并后被删除。 * **FST**:每个字段的词典在每个 segment 中单独维护,采用 FST 进行前缀共享和字节压缩。这样即使同一个 term 在多个 segment 中出现,也会在每个 segment 里拥有独立的映射,查询时只需要在对应 segment 的 FST 中定位即可。 --- ### 3. 查询时到底是怎么遍历 segment 的? 1. **请求入口**      客户端的搜索请求先到达 **协调节点**,协调节点把请求 ...

LLM缓存详解

 可以把“大模型缓存”理解成: 把已经算过的结果(或中间结果)存下来,下次尽量复用 。但这里面其实分几层,不只是简单的“问题→答案”缓存。 1️⃣ 常见的几种缓存类型 (1)KV Cache(推理内部缓存) Transformer 在生成时,会把前面 token 的 Key/Value 向量 缓存下来。 本质:避免重复计算 attention 作用: 同一请求内部加速 特点: 👉 只对“同一上下文继续生成”有效 👉 不跨用户、不跨请求 这类缓存是你体感“流式输出越来越快”的原因之一。 (2)Prompt Cache(提示词缓存) 缓存的是: 相同(或高度相似)的 prompt → 对应的中间表示 / 输出 典型场景: 系统提示词(system prompt)很长 多轮对话里前文基本不变 👉 这里能省掉 前缀计算成本(prefill) (3)Embedding / 语义缓存(Semantic Cache) 这个才是你问题的关键 👇 不是按“字符串完全一致”,而是: 把问题转成向量 → 找“语义相似”的历史问题 → 直接复用答案 2️⃣ 为什么命中缓存成本低很多? 因为大模型推理成本主要在两块: (1)Prefill(吃 prompt) 复杂度 ~ O(n²) 很贵(尤其长 prompt) (2)Decode(逐 token 生成) 每个 token 都要算一遍模型 而缓存命中后: KV cache:不用重复 attention Prompt cache:不用重新 encode 语义缓存: 直接跳过模型推理 👉 相当于从: 几十~几百毫秒 + GPU算力 变成: 一次向量检索(毫秒级)+ 直接返回 所以成本差一个数量级是正常的。 3️⃣ “每个人问法不同,怎么命中缓存?” 这是核心难点,也是工程重点👇 ❌ 不能靠字符串匹配 比如: “今天天气怎么样” “今天外面热不热” 字符串完全不同 → 必须 miss ✅ 用语义相似度(Embedding) 流程一般是: 把问题转 embedding(向量) 在向量数据库里找 TopK 相似问题 如果相似度 > 阈值(比如 0.9) 直接返回缓存答案 一个简单示意 Q1: 北京天气怎么样 → embedding A Q2: 北京今天热吗 → embedding B cosine(A, B) ≈ 0.95...

事务的ACID是什么

 事务的 ACID 是数据库事务必须满足的四个基本性质,用来保证在并发和故障情况下数据的正确性与可靠性: A(Atomicity,原子性) 一个事务中的操作要么 全部成功 ,要么 全部失败回滚 ,不存在“只做了一半”的中间状态。 C(Consistency,一致性) 事务执行前后,数据库都必须处于 一致的合法状态 ,满足约束(如主键、外键、唯一性、业务规则等)。 I(Isolation,隔离性) 并发执行的多个事务之间 相互隔离 ,一个事务未提交的中间结果对其他事务不可见(具体强弱由隔离级别决定)。 D(Durability,持久性) 一旦事务提交成功,其结果会被 永久保存 ,即使系统崩溃也不会丢失(通常依赖 WAL/redo log 等机制)。 一句话记忆: 要么全做完、前后不破坏规则、互不干扰、做完不丢。