不能直接在 init_by_lua* 中改 composer 全局配置,因为 openresty 的 lua 运行于 nginx worker 进程,与 php cli 及其 composer.json 或 ~/.composer/config.json 完全隔离,无法影响 php-fpm 环境;“动态切换镜像源”本质是拦截并重写 http 请求目标,而非修改 php 配置。

为什么不能直接在 init_by_lua* 里改 Composer 的全局配置
因为 OpenResty 的 Lua 运行在 Nginx worker 进程里,而 Composer 是 PHP CLI 工具,两者完全隔离。你改不了 PHP 进程里的 composer.json 或 ~/.composer/config.json,更无法影响下游 PHP-FPM 的执行环境。所谓“动态切换镜像源”,本质是拦截并重写 Composer 的 HTTP 请求目标,不是修改 PHP 配置。
如何用 access_by_lua_block 拦截 Composer 的 packagist.org 请求
Composer 默认从 packagist.org 获取元数据,所有请求都带明确路径前缀(如 /packages.json、/p/monolog/monolog.json)。OpenResty 可以在 access_by_lua_block 中识别这些路径,再用 ngx.var.upstream_http_location 或 ngx.exec 转发到国内镜像(如阿里云 https://mirrors.aliyun.com/composer)。
- 只拦截
GET请求,且ngx.var.uri匹配^/packages\.json$、^/p/.*\.json$、^/dist/.*$等典型 Composer 路径 - 用
string.match(ngx.var.host, "%w+%.%w+")判断是否来自 Composer CLI 的 User-Agent(如Composer/2.7.6),避免误伤正常站点流量 - 镜像源 URL 必须保留原始 path 和 query,例如把
https://packagist.org/p/monolog/monolog.json重写为https://mirrors.aliyun.com/composer/p/monolog/monolog.json - 注意重定向响应头需设置
Location,但 Composer CLI 默认不跟随 302;更稳妥的是用ngx.exec内部跳转,或直接ngx.location.capture代理请求
ngx.location.capture 代理时的常见坑
直接用 ngx.location.capture 向镜像源发起子请求,看似简单,但容易触发超时、证书验证失败、Host 头丢失等问题。
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
- 必须显式设置
headers["Host"] = "mirrors.aliyun.com",否则镜像站可能返回 400 - 默认不继承 SSL 验证,若镜像源 HTTPS 证书异常(比如自签名测试环境),需加
ssl_verify = false参数 - Composer 请求常带
If-Modified-Since和If-None-Match,代理时要原样透传,否则缓存失效 - 响应体较大(如
packages.json超 10MB),需确认lua_http_max_buffer_size和lua_http_read_timeout设置足够(建议至少 30s / 16M)
如何根据客户端 IP 或 Header 动态选镜像源
不是所有用户都适合走同一镜像。比如内部 CI 机器走腾讯云镜像,海外用户回源 packagist.org,开发人员可通过 X-Composer-Mirror Header 强制指定。
- 用
ngx.var.http_x_composer_mirror读取自定义 Header,值为aliyun、tencent、huawei时分别拼接对应域名 - 用
ngx.var.remote_addr查表匹配内网段(如192.168.0.0/16),走高速内网镜像;其他走默认阿里云 - 注意 fallback:当镜像源返回非 2xx 响应时,不要静默失败,而是记录日志并尝试回源 —— 用
if res.status >= 400 then ... end判断
真正麻烦的不是切换逻辑,而是镜像源本身不保证 100% 实时同步。有些包更新后几小时内镜像还没拉取,这时候强行代理反而导致安装失败。得留个开关,比如通过 lua_shared_dict 缓存「最近一次镜像健康检查结果」,不健康的就自动绕过。










