composer镜像站过滤404包请求是为了防止无效请求穿透至上游packagist,避免带宽浪费、ip限流及风控触发;采用bloomfilter-php实现布隆过滤器,支持持久化与热加载,通过解析packagist官方dump导入全量包名,并在api入口拦截非法包名。

为什么 Composer 镜像站要过滤 404 package 请求?
大量不存在的包(比如拼写错误、废弃包、恶意扫描)会直接穿透镜像,打到上游 Packagist,导致带宽浪费、IP 被限流,甚至触发上游风控。布隆过滤器(Bloom Filter)是唯一能在内存可控前提下,对海量包名做「存在性快速否定」的方案——它不存具体包名,只存哈希指纹,查得快、占得少、允许误判(即把不存在的包判为“可能存在”,但绝不会漏掉真实存在的包)。
BloomFilter 选型:用 bitsets 还是 bloomfilter-php?
PHP 生态里真正轻量、支持持久化的只有 bloomfilter-php(基于 murmur3 + bitmap),bitsets 库虽快但不支持序列化,重启即丢;而自建镜像必须支持热加载和增量更新。关键点:
-
bloomfilter-php的add()和contains()是 O(k) 时间复杂度(k=哈希函数个数),默认 k=3,足够应对每秒数千次包名查询 - 初始化时务必指定容量(
$capacity)和误差率($errorRate),例如 1000 万包、误差率 0.01 → 实际需约 19MB 内存,别用默认值硬扛生产流量 - 必须配合
file_put_contents($path, $bf->serialize())定期落盘,否则进程重启后白名单全丢
如何把 Packagist 全量包名导入布隆过滤器?
不能靠 composer show --all 或爬首页——那是动态视图,漏包且不稳定。正确路径是解析 Packagist 官方 dump:
- 下载
https://packagist.org/p2/目录下的所有.json文件(实际是分片索引,含完整包名列表),用curl -s https://packagist.org/packages.json | jq -r '.packages[]'提取主包名数组 - 每读一个包名,调用
$bf->add($packageName);注意跳过以dev-开头的开发分支别名,它们不进正式索引 - 导入完成后立刻
$bf->serialize()并写入共享存储(如 Redis 的SET或本地/var/cache/composer-bf.bin),供所有 PHP-FPM worker 加载
在 Composer API 请求入口拦截 404 package
镜像站的 packages.json 和 /p/{vendor}/{package}.json 两个端点是主战场。拦截逻辑必须放在路由解析之后、上游代理之前:
- 提取请求路径中的包名(如
/p/myorg/foobar.json→myorg/foobar),注意 URL 解码和大小写归一化(Packagist 包名全小写) - 调用
$bf->contains($normalizedPackageName),返回false则直接throw new HttpException(404, 'Package not found'),绝不透传 - 特别注意:Composer 的
require请求可能带版本约束(如/p/myorg/foobar~1.2.json),此时要截掉~1.2后缀再查,否则布隆过滤器永远不命中
布隆过滤器本身不解决「包存在但版本不存在」的问题,那属于下游元数据校验范畴;它的唯一职责,就是守住第一道门——让 99% 的无效包名在 0.1ms 内被拒之门外。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











