内容
firecrawl-rotator
一款小型反向代理,位于 **firecrawl-mcp** 和 Firecrawl API(`api.firecrawl.dev`)之间。它维护一个 Firecrawl API 密钥池,每个请求注入一个密钥,并在拒绝时**轮换到下一个密钥**(尽、速率限制、密钥错误)- 透明重试,以便 MCP 客户端在密钥池耗尽之前永远不会看到密钥级别的失败。
它还会重写 Firecrawl 的 `next` 分页 URL,以便爬取分页流程流回代理并保持轮换。
## 为什么
`firecrawl-mcp` 将所有上游调用转发到 `FIRECRAWL_API_URL`。将该 URL 指向此代理可添加密钥轮换,而无需对 firecrawl-mcp 进行**任何更改** - 运行原版 `npx -y firecrawl-mcp` 即可。
## 运行
```bash
docker compose up -d
```
使用 `docker-compose.yml`:
```yaml
firecrawl-rotator:
build: .
environment:
FIRECRAWL_API_KEYS: "fc-key1,fc-key2,fc-key3"
UPSTREAM: "https://api.firecrawl.dev"
PORT: "8788"
MAX_PASSES: "2"
firecrawl: # 现有的 mcpo + firecrawl-mcp 服务
environment:
FIRECRAWL_API_URL: "http://firecrawl-rotator:8788"
# FIRECRAWL_API_KEY 已移除 - 轮换器注入它
```
## 配置(环境变量)
| 变量 | 默认值 | 目的 |
|-----|---------|---------|
| `FIRECRAWL_API_KEYS` | (必填) | 以逗号分隔的密钥池。 |
|STREAM` | `https://api.firecrawl.dev` | 上游 Firecrawl API 基址。 |
| `UPSTREAM_PROXY` | (未设置) | 出口显式转发代理(`http`/`https`/`socks5`)。优先级高于系统变量。 |
| `HTTPS_PROXY` / `HTTP_PROXY` / `NO_PROXY` | (未设置) | 系统/curl 样式代理环境变量,当 `UPSTREAM_PROXY` 未设置时生效。 |
| `PORT` | `8788` | 监听端口。 |
| `HOST` | `0.0.0.0` | 监听地址。 |
| `MAX_PASSES` | `2` | 密钥池耗尽前的完整轮换次数。 |
| `MAX_BODY_BYTES` | `16777216`(16 MiB) | 缓冲响应正文的上限。超过此限制时,原样转发。`0` = 无限制。 |
| `PROXY_BASE_URL` | (从 `Host` 头部获取) | 重写 `next` URL 的基准 URL。 |
| `LOG_LEVEL` | `info` | `debug` 添加每个请求的日志行。 |
## 端点
- `GET /healthz` -> `200 {"ok":true}` 如果密钥池非空,否则返回 `503`。Docker 健康检查目标 `GET /status 密钥池当前索引、每个密钥的统计信息(码到最后 4 个字符)。
## 轮换行为
在以下情况下轮换:
* HTTP **402**(信用额度)
* **429**(速率限制)
* **401/403**(密钥错误)
* 错误响应体匹配 `insufficient credits`、`rate limit`、`exceeded`、`payment required`、`unauthorized`、`forbidden`(捕获 `200 + success:false`)。
- 每个密钥尝试最多 `MAX_PASSES` 次,然后原样返回最后的- **5xx 和网络触发轮换**(不是)。
- `next` 绝对上游 URL 被重代理,以便爬取分页流程换。响应正文中其他出现的域名**不会**被重它们可能是实际抓取的内容## 开发
```bash
go test ./...
go build -o rotator .
FIRECRAWL_API_KEYS=fc-x ./rotator
```
参见 `docs/superpowers/specs/2026-07-crawl-token-rotation-design.md` 以获取完整设计。