# https://github.com/jina-ai/reader 项目说明书

生成时间：2026-06-20 16:06:53 UTC

## 目录

- [Overview and System Architecture](#page-overview)
- [Read Pipeline: Engines, Extraction and LLM/VLM](#page-read)
- [Search and SERP Integration](#page-search)
- [Deployment, Security, and Common Failure Modes](#page-ops)

<a id='page-overview'></a>

## Overview and System Architecture

### 相关页面

相关主题：[Read Pipeline: Engines, Extraction and LLM/VLM](#page-read), [Search and SERP Integration](#page-search), [Deployment, Security, and Common Failure Modes](#page-ops)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [README.md](https://github.com/jina-ai/reader/blob/main/README.md)
- [package.json](https://github.com/jina-ai/reader/blob/main/package.json)
- [src/api/crawler.ts](https://github.com/jina-ai/reader/blob/main/src/api/crawler.ts)
- [src/services/serp/serper.ts](https://github.com/jina-ai/reader/blob/main/src/services/serp/serper.ts)
- [src/services/serp/compat.ts](https://github.com/jina-ai/reader/blob/main/src/services/serp/compat.ts)
- [src/services/common-llm/open-router.ts](https://github.com/jina-ai/reader/blob/main/src/services/common-llm/open-router.ts)
- [src/services/common-llm/google-gemini.ts](https://github.com/jina-ai/reader/blob/main/src/services/common-llm/google-gemini.ts)
- [src/utils/markdown.ts](https://github.com/jina-ai/reader/blob/main/src/utils/markdown.ts)
- [src/utils/tailwind-classes.ts](https://github.com/jina-ai/reader/blob/main/src/utils/tailwind-classes.ts)
</details>

# 概览与系统架构

## 项目定位与核心能力

`jina-ai/reader`（以下简称 **Reader**）是一个将任意 URL 转换为「LLM 友好」输入的开放源代码服务，同时提供基于 `s.jina.ai` 的网络搜索能力。根据 [README.md](https://github.com/jina-ai/reader/blob/main/README.md) 中的描述，Reader 自定位为：

- **Read**：通过 `https://r.jina.ai/<url>` 抓取并转换目标网页，输出对智能体与 RAG 系统更友好的结构化文本（Markdown、HTML、纯文本等）。
- **Search**：通过 `https://s.jina.ai/<query>` 执行网页搜索，将检索结果以统一格式返回给大模型。

除了 HTML 页面，README 还指出 Reader 支持包括 PDF、Office 文档（经由 LibreOffice 转换）以及图像在内的多种输入类型，图像通过视觉语言模型（VLM）生成 alt 文本以辅助下游 LLM 推理。资料来源：[README.md](https://github.com/jina-ai/reader/blob/main/README.md)

## 系统架构总览

Reader 在运行时呈现为一个 Koa HTTP 服务，对外暴露 h2c（HTTP/2 cleartext，端口 8080）与 HTTP/1.1（端口 8081）双协议入口，二者共享同一套处理路由。服务启动后通过 `civkit` 框架注入依赖，并以 `tsyringe` 进行控制反转（IoC），从而在 `src/api/`、`src/services/`、`src/utils/` 三个主要目录下形成清晰的分层。

```mermaid
flowchart TB
    Client[客户端/CLI/LLM Agent] -->|GET r.jina.ai/URL| Edge[HTTP 入口:8080 h2c / 8081 h1.1]
    Edge --> API{API 路由分发}
    API --> Crawler[src/api/crawler.ts<br/>Read 接口]
    API --> Searcher[src/api/searcher.ts<br/>Search 接口]
    API --> Serp[src/api/serp.ts<br/>SERP 接口]
    Crawler -->|x-engine| Engine{抓取引擎}
    Engine -->|browser| HeadlessChrome[无头 Chrome]
    Engine -->|curl| LibcurlImpersonate[libcurl-impersonate]
    Engine -->|auto| Hybrid[混合策略]
    HeadlessChrome --> Readability[@mozilla/readability]
    LibcurlImpersonate --> Readability
    Hybrid --> Readability
    Readability --> Markdown[src/utils/markdown.ts<br/>tidyMarkdown]
    Markdown --> VLM[common-llm/*<br/>为图像生成 alt]
    VLM --> Resp[text/html/markdown/frontmatter/screenshot]
    Searcher --> SerpProvider[src/services/serp/*]
    SerpProvider --> Resp
    Client -->|GET s.jina.ai/QUERY| Searcher
```

上图揭示了三条主要的数据通道：**Read**（Crawler）、**Search**（Searcher/Serp）以及面向多种 LLM 供应商的 **common-llm** 适配层。

## 核心组件与依赖

下表总结了 Reader 各核心模块在代码库中的位置及其职能，所有路径均可在 [package.json](https://github.com/jina-ai/reader/blob/main/package.json) 中通过 `exports` 字段找到对应的可发布入口（`./crawl`、`./search`、`./serp`）。

| 模块 | 主要源文件 | 关键依赖 | 职责 |
|------|-----------|----------|------|
| HTTP 入口与抓取编排 | `src/api/crawler.ts` | `koa`, `civkit` | 解析请求参数、调度抓取引擎、组装 `textRepresentation` / `frontmatterRepresentation` / `pageshotUrl` 等多种响应形态（资料来源：[src/api/crawler.ts](https://github.com/jina-ai/reader/blob/main/src/api/crawler.ts)）|
| 抓取引擎 | 引擎选择由 `x-engine` 头控制 | `@nomagick/node-libcurl-impersonate`, Playwright/Chrome | 提供 `browser`（JS 渲染）、`curl`（轻量）与 `auto`（组合）三种策略 |
| 内容净化 | `@mozilla/readability` | — | 提取主体内容、去除噪声元素 |
| Markdown 规范化 | `src/utils/markdown.ts` | — | 通过 `tidyMarkdown` 修复跨行链接、压缩空白、统一锚文本（资料来源：[src/utils/markdown.ts](https://github.com/jina-ai/reader/blob/main/src/utils/markdown.ts)）|
| SERP 适配 | `src/services/serp/serper.ts`, `src/services/serp/compat.ts` | `google-auth-library` | 抽象 `WebSearchEntry`、实现 `intitle`/`inurl`/`site` 等 Google 高级搜索操作符（资料来源：[src/services/serp/serper.ts](https://github.com/jina-ai/reader/blob/main/src/services/serp/serper.ts)、[src/services/serp/compat.ts](https://github.com/jina-ai/reader/blob/main/src/services/serp/compat.ts)）|
| 多 LLM 适配 | `src/services/common-llm/open-router.ts`, `src/services/common-llm/google-gemini.ts` | `openai`, `replicate` | 对 OpenAI 兼容接口与 Google Gemini 的消息/函数调用结构进行 Coercible 映射（资料来源：[src/services/common-llm/open-router.ts](https://github.com/jina-ai/reader/blob/main/src/services/common-llm/open-router.ts)、[src/services/common-llm/google-gemini.ts](https://github.com/jina-ai/reader/blob/main/src/services/common-llm/google-gemini.ts)）|
| 样式/渲染清洗 | `src/utils/tailwind-classes.ts` | — | 维护 Tailwind 类名白名单，在快照与 Markdown 生成阶段剥离无用样式类 |
| 运行时框架 | `package.json` | `civkit ^0.10.0`, `tsyringe ^4.10.0`, `undici ^7.24.7` | 依赖注入、RPC 反射、H2/HTTP1 双协议服务器，Node 版本要求 `>=22.15`（资料来源：[package.json](https://github.com/jina-ai/reader/blob/main/package.json)）|

## 请求处理流程

以单 URL 抓取为例，典型的请求生命周期如下：

1. **入口解析**：客户端访问 `https://r.jina.ai/<目标 URL>`，Koa 在 `src/api/crawler.ts` 的控制器内提取路径与请求头中的 `x-respond-with`、`x-engine`、`x-set-cookie`、`x-cache-tolerance`、`x-proxy-url`、`x-preset` 等开关。控制器最终调用 `_attemptURLFix` 修正协议/路径前缀，并构造 `CrawlerOptions`。资料来源：[src/api/crawler.ts](https://github.com/jina-ai/reader/blob/main/src/api/crawler.ts)
2. **抓取阶段**：根据 `x-engine` 选择 `browser` / `curl` / `auto`。浏览器分支会启动无头 Chrome 渲染 SPA；`curl` 分支走 `libcurl-impersonate` 以绕过部分反爬；`auto` 会先尝试 curl，必要时回退到浏览器。
3. **内容净化与规范化**：抓取结果经 `@mozilla/readability` 抽取主体，再由 `tidyMarkdown` 修正链接、空行与图像语法。资料来源：[src/utils/markdown.ts](https://github.com/jina-ai/reader/blob/main/src/utils/markdown.ts)
4. **可选 VLM 增强**：若设置了 `x-with-generated-alt`，图像会被送入 `common-llm` 子系统的视觉语言模型以生成 `![Image [idx]: [VLM_caption]](img_URL)` 形式的提示。资料来源：[README.md](https://github.com/jina-ai/reader/blob/main/README.md)
5. **响应组装**：根据 `x-respond-with` 的取值返回 Markdown / HTML / 纯文本 / 截图 URL / YAML frontmatter 等形式，对应 `src/api/crawler.ts` 中 `respondWith === 'pageshot'`、`respondWith?.includes('frontmatter')` 等分支。资料来源：[src/api/crawler.ts](https://github.com/jina-ai/reader/blob/main/src/api/crawler.ts)

## 部署与社区关切

Reader 官方推荐通过 GitHub Container Registry 拉取预构建镜像 `ghcr.io/jina-ai/reader:oss`，该镜像已集成无头 Chrome、LibreOffice 与 CJK 字体，从而支持 PDF、Office 与亚洲语言页面开箱即用。Docker 部署也是社区呼声最高的方向之一，相关讨论见 issue [#2](https://github.com/jina-ai/reader/issues/2) 与 [#55](https://github.com/jina-ai/reader/issues/55)。

需要关注的是，社区近期报告了若干运行期问题：

- **SSRF 防护**：自托管部署中存在通过 DNS 解析绕过与未对重定向逐跳校验两类风险，见 issue [#1253](https://github.com/jina-ai/reader/issues/1253) 与 [#1252](https://github.com/jina-ai/reader/issues/1252)。建议生产环境启用额外出口过滤。
- **超时与动态内容**：浏览器渲染默认 30 秒超时（issue [#1118](https://github.com/jina-ai/reader/issues/1118)）；SPA/隐藏元素提取仍是待改进点（issue [#1242](https://github.com/jina-ai/reader/issues/1242)）。
- **输出体积**：生成 Markdown 可能大于原始 HTML（issue [#1250](https://github.com/jina-ai/reader/issues/1250)），可通过 `x-md-*`、`x-with-links-summary` 调优。

## See Also

- [配置与请求头选项（README）](https://github.com/jina-ai/reader/blob/main/README.md)
- [Crawler API 源码](https://github.com/jina-ai/reader/blob/main/src/api/crawler.ts)
- [SERP 服务源码](https://github.com/jina-ai/reader/blob/main/src/services/serp/serper.ts)
- [多 LLM 适配层（OpenRouter）](https://github.com/jina-ai/reader/blob/main/src/services/common-llm/open-router.ts)
- [多 LLM 适配层（Gemini）](https://github.com/jina-ai/reader/blob/main/src/services/common-llm/google-gemini.ts)
- [Markdown 规范化工具](https://github.com/jina-ai/reader/blob/main/src/utils/markdown.ts)

---

<a id='page-read'></a>

## Read Pipeline: Engines, Extraction and LLM/VLM

### 相关页面

相关主题：[Overview and System Architecture](#page-overview), [Search and SERP Integration](#page-search), [Deployment, Security, and Common Failure Modes](#page-ops)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [README.md](https://github.com/jina-ai/reader/blob/main/README.md)
- [package.json](https://github.com/jina-ai/reader/blob/main/package.json)
- [src/services/common-llm/open-router.ts](https://github.com/jina-ai/reader/blob/main/src/services/common-llm/open-router.ts)
- [src/services/common-llm/google-gemini.ts](https://github.com/jina-ai/reader/blob/main/src/services/common-llm/google-gemini.ts)
- [src/services/common-llm/misc.ts](https://github.com/jina-ai/reader/blob/main/src/services/common-llm/misc.ts)
- [src/services/serp/serper.ts](https://github.com/jina-ai/reader/blob/main/src/services/serp/serper.ts)
- [src/services/serp/compat.ts](https://github.com/jina-ai/reader/blob/main/src/services/serp/compat.ts)
- [src/utils/markdown.ts](https://github.com/jina-ai/reader/blob/main/src/utils/markdown.ts)
</details>

# Read Pipeline: Engines, Extraction and LLM/VLM

## 概述

Reader 的"读取流水线"负责将任意 URL 转换为 LLM 友好的输入。这条流水线由三层组成：抓取引擎（fetch engine）负责获取远程内容并执行 JavaScript，内容提取与转换层把 HTML 规范化为 Markdown，最后 LLM/VLM 层对图片、搜索查询等做语义增强。README 中将整套机制分为 Read（`r.jina.ai`）与 Search（`s.jina.ai`）两类入口，并通过 `x-engine`、`x-respond-with` 等请求头暴露控制点。资料来源：[README.md:1-50]()。

## 抓取引擎：Browser / Curl / Auto

`x-engine` 请求头决定使用哪条抓取路径，可选值包括 `browser`、`curl` 与默认的 `auto`。README 明确说明：`browser` 使用 headless Chrome 来处理需要 JS 渲染的页面；`curl` 是轻量路径，不执行 JS；`auto` 模式会综合使用两者。Docker 镜像 `ghcr.io/jina-ai/reader:oss` 内置了 headless Chrome、LibreOffice 与 CJK 字体，使得 self-host 时无需再准备这些依赖。资料来源：[README.md:80-130]()。

工程层面的依赖体现了同样的分层：`@mozilla/readability` 负责从 DOM 中提取主体内容，`@nomagick/node-libcurl-impersonate` 提供类 curl 的轻量 HTTP 客户端（可伪造 TLS 指纹），`napi-rs/canvas` 与 `@cocos/...` 等用于在浏览器引擎内做截图与图像后处理。资料来源：[package.json:50-90]()。

当 `x-detach-invisibles` 被设置时，流水线隐式切换到 browser 引擎并禁用缓存，因为它需要执行 JS 才能确定元素最终是否会落到 `display:none`。资料来源：[README.md:130-150]()。

## 内容提取与 Markdown 规范化

抓取完成后，HTML 要先经过 Readability 抽取主区域，再经 Turndown 转成 Markdown。`x-respond-with` 用于选择最终输出形态：`markdown`（跳过 Readability）、`html`（返回 `documentElement.outerHTML`）、`text`（`document.body.innerText`）、`screenshot` / `pageshot`（截图 URL）以及 `frontmatter` / `markdown+frontmatter`（带 YAML frontmatter 的 Markdown 块）。资料来源：[README.md:130-200]()。

`src/utils/markdown.ts` 中的 `tidyMarkdown` 处理跨行断裂的链接与图文混排链接：先用正则把跨多行的 `[text](url)` 折叠为单行，再处理 `[text ![alt](img)](link)` 这种带图片的复合链接。这一步解决社区中常见的"链接断裂、图片只显示为链接"的问题。资料来源：[src/utils/markdown.ts:1-30]()。

社区曾反馈 Reader 输出体积大于原始 HTML（issue #1250），以及部分页面（如同 gov.ca 子页，issue #105）抽取不到内容——这两类问题大多源自 Readability 在 SPA/隐藏标签场景下的局限性（issue #1242）以及 Markdown 化时重复写入文本与 frontmatter。

## LLM / VLM 增强

视觉与语义增强走的是通用 LLM 抽象层。`src/services/common-llm/open-router.ts` 定义了 OpenRouter 的请求模型，覆盖 `temperature`、`top_p`、`top_k`、`frequency_penalty`、`presence_penalty`、`seed`、`tools`、`tool_choice` 等参数；带视觉能力的请求会把 inline 图片以附件方式注入。资料来源：[src/services/common-llm/open-router.ts:1-80]()。

`src/services/common-llm/google-gemini.ts` 实现了 Gemini 的多模态协议，包括 `GeminiInlineDataChunk`（内联 base64 图片）、`GeminiLinkedDataChunk`（`fileUri` 形式的远端文件）以及函数调用结构 `GeminiFunctionCall` / `GeminiFunctionResponse`，从而支持 PDF 等大型文档的引用式输入。资料来源：[src/services/common-llm/google-gemini.ts:1-120]()。

`src/services/common-llm/misc.ts` 暴露 `chatMLEncode` 与 `PromptChunk` 等共享原语，使不同提供方都能落到同一份消息编码与附件管线上，避免在每个 provider 中重复实现工具调用与多模态注入。资料来源：[src/services/common-llm/misc.ts:1-60]()。

调用 LLM 的典型场景是图片说明：README 中的 `X-With-Generated-Alt: true` 头会让流水线把图片以 `![Image [idx]: [VLM_caption]](img_URL)` 的形式注入 Markdown，使下游纯文本 LLM 也能"看"到图片。资料来源：[README.md:1-50]()。

## 流水线时序

```mermaid
sequenceDiagram
    participant U as 用户/Agent
    participant R as Reader (r.jina.ai)
    participant E as Fetch Engine
    participant X as Extraction/Markdown
    participant L as LLM/VLM

    U->>R: GET /<URL> + x-engine / x-respond-with
    R->>E: 选择 browser/curl/auto
    E-->>R: HTML (含 JS 渲染或 curl 直拉)
    R->>X: Readability + Turndown → tidyMarkdown
    alt 含图片或需要 VLM
        X->>L: inline image → caption
        L-->>X: VLM captions
    end
    X-->>U: Markdown / frontmatter / screenshot
```

## 搜索与组合输出

搜索路径（`s.jina.ai`）复用同一套 LLM 抽象。`src/services/serp/serper.ts` 把 `intitle`、`inurl`、`site`、`loc` 等 Google 高级操作符拼装到查询串上；`src/services/serp/compat.ts` 定义了统一的结果结构 `WebSearchEntry`（含 `link`、`title`、`snippet`、`siteLinks`、`variant` 等字段），使得不同 SERP 提供方的输出可以无损落到下游 LLM 提示词中。资料来源：[src/services/serp/serper.ts:1-80]()、资料来源：[src/services/serp/compat.ts:1-15]()。

## 常见失败模式

| 现象 | 触发条件 | 建议处置 |
| --- | --- | --- |
| 抓取 30s 超时（issue #1118） | `auto` 默认走 browser，目标站点响应慢 | 切换 `-H 'x-engine: curl'`，或提高超时 |
| 输出大于原始 HTML（issue #1250） | Markdown 重复写入文本与 frontmatter/链接 | 使用 `x-with-links-summary: all` 控制链接条目 |
| 单页抽不到内容（issue #105、#1242） | SPA 或隐藏元素依赖 JS 交互 | 强制 `x-engine: browser`，必要时设置 `x-detach-invisibles` |
| 站点拦截匿名流量 | `r.jina.ai` 限速或 CDN 拦截 | 配置 API Key，或在 self-host 时调整代理 |
| Reader 输出"全是文字、图变成链接"（issue #1251） | 关闭了图像保留 | 移除或覆盖相关响应头，避免被 markdown 化时丢图 |

## See Also

- 关于完整请求头列表与 `x-preset` 预设，请参考 [README.md](https://github.com/jina-ai/reader/blob/main/README.md)。
- 关于 self-host Docker 部署与端口（h2c 8080 / HTTP/1.1 8081），参见 README 的 "Self-host with Docker" 章节。
- 关于搜索提供方与查询操作符，请阅读 [src/services/serp/serper.ts](https://github.com/jina-ai/reader/blob/main/src/services/serp/serper.ts)。

---

<a id='page-search'></a>

## Search and SERP Integration

### 相关页面

相关主题：[Overview and System Architecture](#page-overview), [Read Pipeline: Engines, Extraction and LLM/VLM](#page-read), [Deployment, Security, and Common Failure Modes](#page-ops)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [README.md](https://github.com/jina-ai/reader/blob/main/README.md)
- [package.json](https://github.com/jina-ai/reader/blob/main/package.json)
- [src/api/searcher.ts](https://github.com/jina-ai/reader/blob/main/src/api/searcher.ts)
- [src/services/serp/compat.ts](https://github.com/jina-ai/reader/blob/main/src/services/serp/compat.ts)
- [src/services/serp/serper.ts](https://github.com/jina-ai/reader/blob/main/src/services/serp/serper.ts)
- [src/services/serp/bing.ts](https://github.com/jina-ai/reader/blob/main/src/services/serp/bing.ts)
- [src/services/serp/google.ts](https://github.com/jina-ai/reader/blob/main/src/services/serp/google.ts)
- [src/services/serp/puppeteer.ts](https://github.com/jina-ai/reader/blob/main/src/services/serp/puppeteer.ts)
</details>

# 搜索与 SERP 集成

## 1. 概览

Reader 项目提供两个核心入口：使用 `https://r.jina.ai/` 前缀对单个 URL 进行抓取并转换为对 LLM 友好的 Markdown；使用 `https://s.jina.ai/` 前缀对查询词进行联网搜索，并在内部把搜索结果的前若干条 URL 复用 `r.jina.ai` 链路继续抓取正文。资料来源：[README.md:42-64]()。

与一般 RAG/Agent 框架中只返回「标题 + URL + 摘要」的做法不同，Reader 会在 SERP 返回后自动深入抓取每个落地页的实际内容，把结果以结构化形式回传。资料来源：[README.md:60-64]()。这意味着搜索服务不仅是一层「网页查询封装」，还需要串联起 SERP 后端、浏览器控制、Reader 抓取与缓存等多个子系统。

## 2. 架构与请求流

搜索服务的主类为 `SearcherHost`，它继承自 `RPCHost`，作为 RPC 入口接收 `s.jina.ai` 路由上的查询。资料来源：[src/api/searcher.ts:31-40]()。`SearcherHost` 注入了 `GoogleSERP`、`GoogleSERPOldFashion`、`CommonGoogleSERP`、`BingSERP` 等多个 SERP 实现以及 `CurlControl`、`JSDomControl` 等抓取工具，通过构造函数容器组装。

SERP 后端在内部都通过 `SERPSpecializedPuppeteerControl` 驱动 headless Chrome 完成搜索页的渲染与解析，必要时会通过 `CurlControl` 做轻量请求。资料来源：[src/services/serp/puppeteer.ts:13-58]()。`controlledScrap` 方法会拦截 `media`、`font`、`image`、`stylesheet` 四类静态资源，仅保留 `document/xhr/fetch/websocket/prefetch` 等文档类请求，以减少带宽与被反爬的概率。

请求从用户到最终 Markdown 的整体流程可概括如下：

```mermaid
flowchart LR
    A[用户查询] --> B[s.jina.ai 路由]
    B --> C[SearcherHost]
    C --> D{SERP 后端选择}
    D --> E[GoogleSERP]
    D --> F[BingSERP]
    D --> G[Serper API]
    E --> H[Puppeteer 渲染]
    F --> H
    G --> C
    H --> I[WebSearchEntry 列表]
    I --> J[r.jina.ai Reader 抓取]
    J --> K[Markdown/HTML 输出]
```

`SearcherHost` 内部还内置了 7 天的结果缓存窗口（`cacheRetentionMs = 1000 * 3600 * 24 * 7`），并加载 `WORLD_COUNTRIES`、`WORLD_LANGUAGES` 等地理与语言表，用于按区域/语种过滤 SERP 结果。资料来源：[src/api/searcher.ts:38-44]()。

## 3. 数据模型与查询语法

所有 SERP 后端对外暴露统一的 `WebSearchEntry` 接口，字段涵盖 `link`、`title`、`source`、`date`、`snippet`、`imageUrl`、`siteLinks` 与 `variant`（`web` / `images` / `news`），从而屏蔽不同搜索引擎在 DOM 结构上的差异。资料来源：[src/services/serp/compat.ts:3-13]()。

当用户传入 Google 风格的高级操作符（如 `site:`、`intitle:`、`loc:`）时，`GoogleSearchExplicitOperatorsDto` 会把这些字段自动追加到 `searchTerm` 中。`addTo` 方法用 `OR` 拼接多值、用 `AND` 拼接多字段，并在最终查询外包一层括号。资料来源：[src/services/serp/serper.ts:43-78]()。例如多个 `site:` 会被组合为 `site:a.com OR site:b.com`，并和查询词以空格连接。`site` 字段还支持数组形式，从而让 `s.jina.ai` 能一次检索多个站点的并集。资料来源：[README.md:66-72]()。

## 4. 后端实现与运行环境

`GoogleSERP` 与 `BingSERP` 的实现高度对称：均继承 `AsyncService`，都通过 `OVERRIDE_GOOGLE_DOMAIN` / `OVERRIDE_BING_DOMAIN` 环境变量可替换默认域名（分别为 `www.google.com` 和 `www.bing.com`），并用 `generic-pool` 维护可复用的 `BrowserContext` 池。资料来源：[src/services/serp/google.ts:38-44]()、资料来源：[src/services/serp/bing.ts:36-42]()。`GoogleSERP` 的 `SerpContext` 还包含 `magicId` 与 `ua`，便于注入符合 Google 反爬预期的指纹；`BingSERP` 则侧重 `proxyUrl` 与 `validTill` 来管理代理生命周期。

两者的代理能力都通过 `SERPProxyProviderService` 注入，必要时切换出口 IP。Puppeteer 拦截逻辑位于 `controlledScrap`，可在请求级别注入 `extraHeaders`，并通过 `req.continue` 的 priority 字段让自定义头优先于默认头。资料来源：[src/services/serp/puppeteer.ts:14-58]()。

## 5. 部署与社区关注

`SearcherHost` 被打包为 `package.json` 中 `./search` 与 `./serp` 两条独立子入口，方便在自托管时按需引入搜索能力。资料来源：[package.json:46-58]()。社区中 #2、#43、#55 等多个高互动 Issue 都聚焦在「本地/Docker 独立部署」，README 也明确给出了 `ghcr.io/jina-ai/reader:oss` 镜像以及 h2c 8080 与 HTTP/1.1 8081 双端口的启动方式。资料来源：[README.md:96-108]()。

需要注意的是，搜索服务的可用性高度依赖上游搜索引擎与 DNS 环境。Issue #1202 报告过 SERP 路径下出现 100 秒级超时（#1202）；Issue #1237 进一步指出 `jina.ai` 域名在国内遭 DNS 污染，官方建议用户切换到 `r.jinaai.cn` / `s.jinaai.cn`，或通过本地 HOST 绑定源站 IP 来绕过。资料来源：[issue #1237]()。这两条社区线索意味着：自托管或在国内使用 `s.jina.ai` 时，务必在反代层配置备用上游与健康检查，并对 `SERP` 后端做好失败回退。

## See Also

- [Reader API 输入格式与请求头](./README.md)
- [Cookbooks / 预设与抓取示例](./cookbooks.md)
- [Docker 自托管与端口说明](./README.md#self-host-with-docker)

---

<a id='page-ops'></a>

## Deployment, Security, and Common Failure Modes

### 相关页面

相关主题：[Overview and System Architecture](#page-overview), [Read Pipeline: Engines, Extraction and LLM/VLM](#page-read), [Search and SERP Integration](#page-search)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [README.md](https://github.com/jina-ai/reader/blob/main/README.md)
- [package.json](https://github.com/jina-ai/reader/blob/main/package.json)
- [Dockerfile](https://github.com/jina-ai/reader/blob/main/Dockerfile)
- [docker-compose.yml](https://github.com/jina-ai/reader/blob/main/docker-compose.yml)
- [src/utils/markdown.ts](https://github.com/jina-ai/reader/blob/main/src/utils/markdown.ts)
- [src/services/serp/compat.ts](https://github.com/jina-ai/reader/blob/main/src/services/serp/compat.ts)
- [src/services/serp/serper.ts](https://github.com/jina-ai/reader/blob/main/src/services/serp/serper.ts)
- [src/services/common-llm/open-router.ts](https://github.com/jina-ai/reader/blob/main/src/services/common-llm/open-router.ts)
- [src/services/common-iminterrogate/llms.ts](https://github.com/jina-ai/reader/blob/main/src/services/common-iminterrogate/llms.ts)
- [src/services/common-llm/google-gemini.ts](https://github.com/jina-ai/reader/blob/main/src/services/common-llm/google-gemini.ts)
- [src/services/common-iminterrogate/instruct-blip.ts](https://github.com/jina-ai/reader/blob/main/src/services/common-iminterrogate/instruct-blip.ts)
</details>

# Deployment, Security, and Common Failure Modes

本页面向运维与平台工程师说明 `jina-ai/reader` 在自托管（self-host）场景下的部署入口、安全边界，以及社区反馈中已被反复报告的失败模式。重点覆盖三个主题：基于 Docker 的部署方式、SSRF（Server-Side Request Forgery，服务端请求伪造）相关的安全注意事项，以及最常见的用户感知故障与应对策略。

## 1. 部署（Deployment）

`jina-ai/reader` 提供两条使用路径：调用托管的 `https://r.jina.ai/` 与 `https://s.jina.ai/` 端点，或者通过 GitHub Container Registry 上的预构建镜像自行运行。开源分支的镜像名为 `ghcr.io/jina-ai/reader:oss`，镜像内部已捆绑 headless Chrome、LibreOffice 和 CJK 字体，避免用户在宿主机上再单独安装依赖 资料来源：[README.md:39-52]()。

容器对外暴露两个端口：

| 端口 | 协议 | 适用场景 |
| --- | --- | --- |
| `8080` | h2c（HTTP/2 cleartext） | 生产级多路复用，与 Cloud Run 一致；`curl` 默认无法直连，需加 `--http2-prior-knowledge` |
| `8081` | HTTP/1.1 | 浏览器或简单脚本的快速验证入口，路由与 `8080` 完全一致 |

社区历史工单显示，本地 Docker 部署是最高频的诉求之一：issue #2「support docker deployment」和 #55「How do I deploy it locally with docker?」均围绕此话题 资料来源：[README.md:47-52]()。

### Node 运行时与依赖

`package.json` 声明了 `engines.node >= 22.15`，并把 [`puppeteer@^24.42.0`](https://github.com/jina-ai/reader/blob/main/package.json) 作为浏览器抓取引擎，把 [`undici@^7.24.7`](https://github.com/jina-ai/reader/blob/main/package.json) 用作底层 HTTP 客户端，这意味着自托管镜像的网络栈与 Cloud Run 上的实现保持一致。`package.json` 的 `exports` 字段进一步将构建产物以子路径暴露：`.`、`./crawl`、`./search`、`./serp`，便于 SDK 集成 资料来源：[package.json:13-18]()。

## 2. 安全（Security）

由于 Reader 在服务端抓取用户提交的任意 URL，SSRF 是首要风险模型。社区近期收到了两个相互独立的安全报告：

- **issue #1252**：未鉴权 SSRF——`MiscService.assertNormalizedUrl` 只对用户提交的首个 URL 做一次域名/IP 校验，之后跟随 HTTP 重定向时并未逐跳重新验证，攻击者可让 Reader 命中内部地址 资料来源：[issue #1252]()。
- **issue #1253**：自托管部署下的 DNS 解析绕过——通过让域名解析到内部 IP 即可绕过非公开网段的拒绝逻辑；受影响版本为 `commit 1574bfd` 资料来源：[issue #1253]()。

两条工单共同提示：自托管部署方必须在网络层（出站 ACL、VPC firewall）以及应用层双重把关，不能假设 `assertNormalizedUrl` 已经覆盖所有攻击面。

### 风险模型与设计意图

`src/services/common-llm/open-router.ts`、`src/services/common-llm/google-gemini.ts` 以及 `src/services/common-iminterrogate/instruct-blip.ts`、`src/services/common-iminterrogate/llms.ts` 共同构成了「LLM 调用 + VLM 图像描述」的子层，它们把外部模型作为受信任下游 资料来源：[open-router.ts:1-40]()、[google-gemini.ts:1-30]()、[instruct-blip.ts:1-25]()、[llms.ts:1-20]()。这意味着任何抓取到的 HTML/PDF/图片都会原样进入提示词管道，因而强化上游的 URL 校验比在 LLM 层做兜底更有效。

## 3. 常见失败模式（Common Failure Modes）

下面列举社区工单中高频出现的现象及根因归类，便于读者在自托管环境中快速定位。

```mermaid
graph LR
    A[用户请求] --> B{URL 校验}
    B -- 通过 --> C{引擎选择}
    C -- curl --> D[undici 抓取]
    C -- browser --> E[Puppeteer]
    D --> F{Readability / tidyMarkdown}
    E --> F
    F --> G[Markdown 输出]
    B -- 重定向/内部 IP --> H[SSRF 拦截]
    E -- 30s 超时 --> I[TimeoutError 42206]
    F -- 动态内容空 --> J[提取失败]
```

- **抓取超时**：浏览器引擎默认 30 秒导航超时，慢站会返回 `AssertionFailureError` / `TimeoutError`、状态码 `42206`，典型工单为 issue #1118 资料来源：[issue #1118]()。
- **图片以链接形式渲染**：issue #1251 报告「页面只剩纯文本，图片退化为链接」，与 `src/utils/markdown.ts` 中的 `tidyMarkdown` 行为相关——复杂跨行 `[text](url)` 会被正则折叠成单行，若原始 HTML 中 `<img>` 被包裹在多行 `<a>` 内，可能被改写为链接形式 资料来源：[markdown.ts:1-18]()。
- **输出体积反而大于原始 HTML**：issue #1250 指出 Markdown 比 Raw HTML 更大，常见原因是 `x-with-links-summary: all` 或 `x-with-generated-alt` 等扩展头被默认开启，附加了链接摘要与图像描述 资料来源：[issue #1250]()。
- **简单页面提取为空**：issue #105 显示 `canada.ca` 类静态站点有时返回空内容，可能与 Readability 算法对低密度正文页的评分失败有关。
- **动态/隐藏元素**：issue #1242 反馈 SPA、Tab 与 Accordion 后的内容无法被拉取，需要切换至 `x-engine: browser` 或自定义等待策略。
- **SERP 搜索超时**：issue #1202 反馈开启 SERP 后频繁 100s 超时，根因在 `src/services/serp/serper.ts` 和 `compat.ts` 的外部查询通道 资料来源：[serper.ts:1-20]()、[compat.ts:1-8]()。
- **DNS 污染/地域封禁**：issue #1237 报告 `jina.ai` 在境内被 DNS 污染，官方建议改用 `r.jinaai.cn` 与 `s.jinaai.cn` 过渡。

### 缓解建议

1. 出站防火墙仅放行公网段，并对 `localhost`、`169.254.0.0/16`、`10.0.0.0/8` 等保留网段做兜底拒绝，弥补 `assertNormalizedUrl` 的单跳校验缺陷。
2. 通过 `x-engine: curl` + 合适的 `x-timeout` 头绕开浏览器 30s 默认值；对必须 JS 渲染的页面，再启用浏览器引擎并结合 `x-wait-for-selector`。
3. 默认开启 `x-respond-with: markdown` 而非 `frontmatter`，可显著降低输出膨胀 资料来源：[README.md:1-12]()。
4. 自托管前务必升级到修复 #1252、#1253 的提交；并在 CI 中加入重定向回归测试。

## See Also

- [README.md](https://github.com/jina-ai/reader/blob/main/README.md)
- [cookbooks.md](https://github.com/jina-ai/reader/blob/main/cookbooks.md)
- [package.json](https://github.com/jina-ai/reader/blob/main/package.json)

---

<!-- evidence_pipeline_checked: true -->
<!-- evidence_injected: true -->

---

## Doramagic 踩坑日志

项目：jina-ai/reader

摘要：发现 28 个潜在踩坑项，其中 2 个为 high/blocking；最高优先级：安全/权限坑 - 失败模式：security_permissions: Server-Side Request Forgery via domain resolution bypass in self-hosted deployments。

## 1. 安全/权限坑 · 失败模式：security_permissions: Server-Side Request Forgery via domain resolution bypass in self-hosted deployments

- 严重度：high
- 证据强度：source_linked
- 发现：Developers should check this security_permissions risk before relying on the project: Server-Side Request Forgery via domain resolution bypass in self-hosted deployments
- 对用户的影响：Developers may expose sensitive permissions or credentials: Server-Side Request Forgery via domain resolution bypass in self-hosted deployments
- 证据：failure_mode_cluster:github_issue | https://github.com/jina-ai/reader/issues/1253 | Server-Side Request Forgery via domain resolution bypass in self-hosted deployments

## 2. 安全/权限坑 · 失败模式：security_permissions: Unauthenticated SSRF via unvalidated HTTP redirects (single-shot SSRF gate not re-applied per...

- 严重度：high
- 证据强度：source_linked
- 发现：Developers should check this security_permissions risk before relying on the project: Unauthenticated SSRF via unvalidated HTTP redirects (single-shot SSRF gate not re-applied per redirect hop)
- 对用户的影响：Developers may expose sensitive permissions or credentials: Unauthenticated SSRF via unvalidated HTTP redirects (single-shot SSRF gate not re-applied per redirect hop)
- 证据：failure_mode_cluster:github_issue | https://github.com/jina-ai/reader/issues/1252 | Unauthenticated SSRF via unvalidated HTTP redirects (single-shot SSRF gate not re-applied per redirect hop)

## 3. 安装坑 · 依赖 Docker 环境

- 严重度：medium
- 证据强度：runtime_trace
- 发现：安装/运行入口包含 Docker 命令：docker run --rm -p 3000:8081 ghcr.io/jina-ai/reader:oss # then: curl http://localhost:3000/https://example.com
- 对用户的影响：非工程用户可能没有 Docker，启动成本明显增加。
- 复现命令：`docker run --rm -p 3000:8081 ghcr.io/jina-ai/reader:oss # then: curl http://localhost:3000/https://example.com`
- 证据：identity.distribution | https://github.com/jina-ai/reader | docker run --rm -p 3000:8081 ghcr.io/jina-ai/reader:oss # then: curl http://localhost:3000/https://example.com

## 4. 安装坑 · 失败模式：installation: npm run build failed because shared files are not found

- 严重度：medium
- 证据强度：source_linked
- 发现：Developers should check this installation risk before relying on the project: npm run build failed because shared files are not found
- 对用户的影响：Developers may fail before the first successful local run: npm run build failed because shared files are not found
- 证据：failure_mode_cluster:github_issue | https://github.com/jina-ai/reader/issues/3 | npm run build failed because shared files are not found

## 5. 安装坑 · 来源证据：npm run build failed because shared files are not found

- 严重度：medium
- 证据强度：source_linked
- 发现：GitHub 社区证据显示该项目存在一个安装相关的待验证问题：npm run build failed because shared files are not found
- 对用户的影响：可能阻塞安装或首次运行。
- 证据：community_evidence:github | https://github.com/jina-ai/reader/issues/3 | 来源讨论提到 npm 相关条件，需在安装/试用前复核。

## 6. 安装坑 · 来源证据：support docker deployment

- 严重度：medium
- 证据强度：source_linked
- 发现：GitHub 社区证据显示该项目存在一个安装相关的待验证问题：support docker deployment
- 对用户的影响：可能增加新用户试用和生产接入成本。
- 证据：community_evidence:github | https://github.com/jina-ai/reader/issues/2 | 来源讨论提到 docker 相关条件，需在安装/试用前复核。

## 7. 配置坑 · 失败模式：configuration: Improve content extraction logic to handle dynamic and hidden elements

- 严重度：medium
- 证据强度：source_linked
- 发现：Developers should check this configuration risk before relying on the project: Improve content extraction logic to handle dynamic and hidden elements
- 对用户的影响：Developers may misconfigure credentials, environment, or host setup: Improve content extraction logic to handle dynamic and hidden elements
- 证据：failure_mode_cluster:github_issue | https://github.com/jina-ai/reader/issues/1242 | Improve content extraction logic to handle dynamic and hidden elements

## 8. 配置坑 · 失败模式：configuration: Respect robots.txt and identify your system

- 严重度：medium
- 证据强度：source_linked
- 发现：Developers should check this configuration risk before relying on the project: Respect robots.txt and identify your system
- 对用户的影响：Developers may misconfigure credentials, environment, or host setup: Respect robots.txt and identify your system
- 证据：failure_mode_cluster:github_issue | https://github.com/jina-ai/reader/issues/4 | Respect robots.txt and identify your system

## 9. 配置坑 · 失败模式：configuration: support docker deployment

- 严重度：medium
- 证据强度：source_linked
- 发现：Developers should check this configuration risk before relying on the project: support docker deployment
- 对用户的影响：Developers may misconfigure credentials, environment, or host setup: support docker deployment
- 证据：failure_mode_cluster:github_issue | https://github.com/jina-ai/reader/issues/2 | support docker deployment

## 10. 能力坑 · 能力判断依赖假设

- 严重度：medium
- 证据强度：source_linked
- 发现：README/documentation is current enough for a first validation pass.
- 对用户的影响：假设不成立时，用户拿不到承诺的能力。
- 证据：capability.assumptions | https://github.com/jina-ai/reader | README/documentation is current enough for a first validation pass.

## 11. 运行坑 · 失败模式：runtime: Failed to go to

- 严重度：medium
- 证据强度：source_linked
- 发现：Developers should check this runtime risk before relying on the project: Failed to go to
- 对用户的影响：Developers may hit a documented source-backed failure mode: Failed to go to
- 证据：failure_mode_cluster:github_issue | https://github.com/jina-ai/reader/issues/1118 | Failed to go to

## 12. 运行坑 · 失败模式：runtime: Reader doesn't extract any content from this page even though its quite simple?

- 严重度：medium
- 证据强度：source_linked
- 发现：Developers should check this runtime risk before relying on the project: Reader doesn't extract any content from this page even though its quite simple?
- 对用户的影响：Developers may hit a documented source-backed failure mode: Reader doesn't extract any content from this page even though its quite simple?
- 证据：failure_mode_cluster:github_issue | https://github.com/jina-ai/reader/issues/105 | Reader doesn't extract any content from this page even though its quite simple?

## 13. 运行坑 · 来源证据：Failed to go to

- 严重度：medium
- 证据强度：source_linked
- 发现：GitHub 社区证据显示该项目存在一个运行相关的待验证问题：Failed to go to
- 对用户的影响：可能增加新用户试用和生产接入成本。
- 证据：community_evidence:github | https://github.com/jina-ai/reader/issues/1118 | 来源类型 github_issue 暴露的待验证使用条件。

## 14. 维护坑 · 维护活跃度未知

- 严重度：medium
- 证据强度：source_linked
- 发现：未记录 last_activity_observed。
- 对用户的影响：新项目、停更项目和活跃项目会被混在一起，推荐信任度下降。
- 证据：evidence.maintainer_signals | https://github.com/jina-ai/reader | last_activity_observed missing

- 严重度：medium
- 证据强度：source_linked
- 发现：no_demo
- 证据：downstream_validation.risk_items | https://github.com/jina-ai/reader | no_demo; severity=medium

## 16. 安全/权限坑 · 存在评分风险

- 严重度：medium
- 证据强度：source_linked
- 发现：no_demo
- 对用户的影响：风险会影响是否适合普通用户安装。
- 证据：risks.scoring_risks | https://github.com/jina-ai/reader | no_demo; severity=medium

## 17. 安全/权限坑 · 来源证据：Bug/Optimization: Reader Output size is larger than Raw HTML size

- 严重度：medium
- 证据强度：source_linked
- 发现：GitHub 社区证据显示该项目存在一个安全/权限相关的待验证问题：Bug/Optimization: Reader Output size is larger than Raw HTML size
- 对用户的影响：可能影响授权、密钥配置或安全边界。
- 证据：community_evidence:github | https://github.com/jina-ai/reader/issues/1250 | 来源类型 github_issue 暴露的待验证使用条件。

## 18. 安全/权限坑 · 来源证据：Improve content extraction logic to handle dynamic and hidden elements

- 严重度：medium
- 证据强度：source_linked
- 发现：GitHub 社区证据显示该项目存在一个安全/权限相关的待验证问题：Improve content extraction logic to handle dynamic and hidden elements
- 对用户的影响：可能影响授权、密钥配置或安全边界。
- 证据：community_evidence:github | https://github.com/jina-ai/reader/issues/1242 | 来源类型 github_issue 暴露的待验证使用条件。

## 19. 安全/权限坑 · 来源证据：Server-Side Request Forgery via domain resolution bypass in self-hosted deployments

- 严重度：medium
- 证据强度：source_linked
- 发现：GitHub 社区证据显示该项目存在一个安全/权限相关的待验证问题：Server-Side Request Forgery via domain resolution bypass in self-hosted deployments
- 对用户的影响：可能阻塞安装或首次运行。
- 证据：community_evidence:github | https://github.com/jina-ai/reader/issues/1253 | 来源讨论提到 node 相关条件，需在安装/试用前复核。

## 20. 安全/权限坑 · 来源证据：Unauthenticated SSRF via unvalidated HTTP redirects (single-shot SSRF gate not re-applied per redirect hop)

- 严重度：medium
- 证据强度：source_linked
- 发现：GitHub 社区证据显示该项目存在一个安全/权限相关的待验证问题：Unauthenticated SSRF via unvalidated HTTP redirects (single-shot SSRF gate not re-applied per redirect hop)
- 对用户的影响：可能影响授权、密钥配置或安全边界。
- 证据：community_evidence:github | https://github.com/jina-ai/reader/issues/1252 | 来源讨论提到 node 相关条件，需在安装/试用前复核。

## 21. 安全/权限坑 · 来源证据：support docker deployment

- 严重度：medium
- 证据强度：source_linked
- 发现：GitHub 社区证据显示该项目存在一个安全/权限相关的待验证问题：support docker deployment
- 对用户的影响：可能影响授权、密钥配置或安全边界。
- 证据：community_evidence:github | https://github.com/jina-ai/reader/issues/2 | 来源讨论提到 docker 相关条件，需在安装/试用前复核。

## 22. 能力坑 · 失败模式：capability: Bug/Optimization: Reader Output size is larger than Raw HTML size

- 严重度：low
- 证据强度：source_linked
- 发现：Developers should check this capability risk before relying on the project: Bug/Optimization: Reader Output size is larger than Raw HTML size
- 对用户的影响：Developers may hit a documented source-backed failure mode: Bug/Optimization: Reader Output size is larger than Raw HTML size
- 证据：failure_mode_cluster:github_issue | https://github.com/jina-ai/reader/issues/1250 | Bug/Optimization: Reader Output size is larger than Raw HTML size

## 23. 能力坑 · 失败模式：capability: Extraction didn't work

- 严重度：low
- 证据强度：source_linked
- 发现：Developers should check this capability risk before relying on the project: Extraction didn't work
- 对用户的影响：Developers may hit a documented source-backed failure mode: Extraction didn't work
- 证据：failure_mode_cluster:github_issue | https://github.com/jina-ai/reader/issues/1 | Extraction didn't work

## 24. 能力坑 · 失败模式：capability: Jina Reader 和 Search 被墙了

- 严重度：low
- 证据强度：source_linked
- 发现：Developers should check this capability risk before relying on the project: Jina Reader 和 Search 被墙了
- 对用户的影响：Developers may hit a documented source-backed failure mode: Jina Reader 和 Search 被墙了
- 证据：failure_mode_cluster:github_issue | https://github.com/jina-ai/reader/issues/1237 | Jina Reader 和 Search 被墙了

## 25. 能力坑 · 失败模式：capability: Pile in reader format

- 严重度：low
- 证据强度：source_linked
- 发现：Developers should check this capability risk before relying on the project: Pile in reader format
- 对用户的影响：Developers may hit a documented source-backed failure mode: Pile in reader format
- 证据：failure_mode_cluster:github_issue | https://github.com/jina-ai/reader/issues/5 | Pile in reader format

## 26. 能力坑 · 失败模式：capability: В странном виде сайты открываются.

- 严重度：low
- 证据强度：source_linked
- 发现：Developers should check this capability risk before relying on the project: В странном виде сайты открываются.
- 对用户的影响：Developers may hit a documented source-backed failure mode: В странном виде сайты открываются.
- 证据：failure_mode_cluster:github_issue | https://github.com/jina-ai/reader/issues/1251 | В странном виде сайты открываются.

## 27. 维护坑 · issue/PR 响应质量未知

- 严重度：low
- 证据强度：source_linked
- 发现：issue_or_pr_quality=unknown。
- 对用户的影响：用户无法判断遇到问题后是否有人维护。
- 证据：evidence.maintainer_signals | https://github.com/jina-ai/reader | issue_or_pr_quality=unknown

## 28. 维护坑 · 发布节奏不明确

- 严重度：low
- 证据强度：source_linked
- 发现：release_recency=unknown。
- 对用户的影响：安装命令和文档可能落后于代码，用户踩坑概率升高。
- 证据：evidence.maintainer_signals | https://github.com/jina-ai/reader | release_recency=unknown

<!-- canonical_name: jina-ai/reader; human_manual_source: deepwiki_human_wiki -->
