net::ERR_HTTP2_PROTOCOL_ERROR
之前处理过 2 Enabled Constant Trigger net ERR_HTTP2_PROTOCOL_ERROR 200 (OK) on Chrome,这次在腾讯云服务器上反向代理的网站上也遇到了这个问题。
在 EdgeOne 上开启了以 HTTP/2 协议进行回源
关闭腾讯云双因素身份验证
每次登录都得收个手机短信验证码,关键信任设备时间最大才只有 7 天,还不支持 Passkey,真是拉胯。为了方便,我选择关闭 2FA。
从 DevTools 网络面板中过滤扩展程序请求
Hexo source/_data 目录中无法使用 NexT 函数的错误
之前为了调整备案文本的显示,把 NexT 模板中 footer 里与备案相关的代码复制到自己的 source/_data/footer.njk 文件了,内容大致为
<div class="beian">
{{- next_url('https://beian.miit.gov.cn', theme.footer.beian.icp + ' ') }}
{%- if theme.footer.beian.gongan_icon_url %}
<img src="{{ url_for(theme.footer.beian.gongan_icon_url) }}" alt="">
{%- endif %}
{%- if theme.footer.beian.gongan_id and theme.footer.beian.gongan_num %}
{{- next_url('https://beian.mps.gov.cn/#/query/webSearch?code=' + theme.footer.beian.gongan_id, theme.footer.beian.gongan_num + ' ') }}
{%- endif %}
</div>但构建项目时提示 Unable to call `next_url`, which is undefined or falsey。虽然结果最后是成功的,但总是有个黄色的 ERROR 看着很不爽。
INFO Start processing
ERROR Process failed: _data/footer.njk
Template render error: (G:\GitHub\enihsyou-blog\source\_data\footer.njk) [Line 6, Column 16]
Error: Unable to call `next_url`, which is undefined or falsey
at Object._prettifyError (G:\GitHub\enihsyou-blog\node_modules\.pnpm\nunjucks@3.2.4_chokidar@3.6.0\node_modules\nunjucks\src\lib.js:32:11)
at G:\GitHub\enihsyou-blog\node_modules\.pnpm\nunjucks@3.2.4_chokidar@3.6.0\node_modules\nunjucks\src\environment.js:464:19
at Template.root [as rootRenderFunc] (eval at _compile (G:\GitHub\enihsyou-blog\node_modules\.pnpm\nunjucks@3.2.4_chokidar@3.6.0\node_modules\nunjucks\src\environment.js:527:18), <anonymous>:24:3)
at Template.render (G:\GitHub\enihsyou-blog\node_modules\.pnpm\nunjucks@3.2.4_chokidar@3.6.0\node_modules\nunjucks\src\environment.js:454:10)
at Hexo.njkRenderer (G:\GitHub\enihsyou-blog\node_modules\.pnpm\hexo@7.3.0_chokidar@3.6.0\node_modules\hexo\dist\plugins\renderer\nunjucks.js:60:29)
at Hexo.tryCatcher (G:\GitHub\enihsyou-blog\node_modules\.pnpm\bluebird@3.7.2\node_modules\bluebird\js\release\util.js:16:23)
at Hexo.<anonymous> (G:\GitHub\enihsyou-blog\node_modules\.pnpm\bluebird@3.7.2\node_modules\bluebird\js\release\method.js:15:34)
at G:\GitHub\enihsyou-blog\node_modules\.pnpm\hexo@7.3.0_chokidar@3.6.0\node_modules\hexo\dist\hexo\render.js:74:28
at tryCatcher (G:\GitHub\enihsyou-blog\node_modules\.pnpm\bluebird@3.7.2\node_modules\bluebird\js\release\util.js:16:23)
at Promise._settlePromiseFromHandler (G:\GitHub\enihsyou-blog\node_modules\.pnpm\bluebird@3.7.2\node_modules\bluebird\js\release\promise.js:547:31)
at Promise._settlePromise (G:\GitHub\enihsyou-blog\node_modules\.pnpm\bluebird@3.7.2\node_modules\bluebird\js\release\promise.js:604:18)
at Promise._settlePromise0 (G:\GitHub\enihsyou-blog\node_modules\.pnpm\bluebird@3.7.2\node_modules\bluebird\js\release\promise.js:649:10)
at Promise._settlePromises (G:\GitHub\enihsyou-blog\node_modules\.pnpm\bluebird@3.7.2\node_modules\bluebird\js\release\promise.js:729:18)
at _drainQueueStep (G:\GitHub\enihsyou-blog\node_modules\.pnpm\bluebird@3.7.2\node_modules\bluebird\js\release\async.js:93:12)
at _drainQueue (G:\GitHub\enihsyou-blog\node_modules\.pnpm\bluebird@3.7.2\node_modules\bluebird\js\release\async.js:86:9)
at Async._drainQueues (G:\GitHub\enihsyou-blog\node_modules\.pnpm\bluebird@3.7.2\node_modules\bluebird\js\release\async.js:102:5)
INFO Files loaded in 361 ms源码调试一波,最终是定位到原因
- 在刚启动后,hexo/lib/plugins/processor/data.ts · hexojs/hexo 会加载
source/_data/下的文件并按后缀名render()一遍 - 看上去渲染结果最后被 hexo/lib/hexo/index.ts · hexojs/hexo 放到
locals中给后续动作用,但这个时候还没加载主题插件,也就拿不到next_url,产生错误 - 后续加载主题,会把函数注册上再重新渲染一遍,最终得到正确的结果
知道原因后解决方式就有了,虽然 NexT文档 说应该那么放,但有时得反着来,不要把模板文件放在 source/_data/ 下 😅
custom_file_path:
head: source/_data/head.njk
footer: source/_datanext/footer.njk从外部连接容器中的 Redis
之前 Umami 服务是用 --extra-host redis:host-gateway 的方式连接部署在主机上的 Redis-Server。运行起来感觉多个服务共用同一个实例总是不好的,就感觉会冲突。虽然有 DB 概念,但大家不用。
host-gateway在 docker container run | Docker Docs 文档中只是简单提到了一嘴,但用来连接主机服务很有用。在 Docker Desktop 中自带了host.docker.internal
所以现在想的是 docker-compose 内起个 redis。但我又希望从主机能连接到容器中,同时不采用 port-forward 来占用主机的端口。
在 Linux 中 bridge network 默认情况下给每个容器都分配了独立的 IP,主机自带路由可以直连访问容器。同时 redis 的 Dockerfile 智能地禁用了 protected mode 且监听 *:6379。
在 ps 中看 Docker 容器的 redis 进程会显示
redis-server *:6379,这个 bind 参数从未在哪指定过却出现了,其实是配置文件中的set-proc-title和proc-title-template在起作用。
又因为容器默认不提供任何 redis.conf,所以根据 bind 的文档说明,会监听所有网卡的默认 6379 端口。
这样一来从主机访问 172.18.0.2:6379 就能直达容器,完美解决问题。至于 Docker compose 可以这么写
services:
umami:
image: docker.umami.is/umami-software/umami:mysql-latest
environment:
REDIS_URL: redis://default:${REDIS_PASSWORD}@redis:6379
depends_on:
- redis
networks:
- umami_net
redis:
image: redis:7-alpine
command: ["redis-server", "--requirepass", "${REDIS_PASSWORD}"]
networks:
- umami_net
networks:
umami_net:如果想要从宝塔面板管理远程服务器,就需要设置 redis 管理密码。
再在主机上 redis-cli -h $(docker container inspect umami-redis-1 -f '{{.NetworkSettings.Networks.umami_umami_net.IPAddress}}') 连接到容器
Umami IP 定位到南非 Pretoria
在网站上安装了自行部署的 Umami 之后,我的访问用户定位在南非的 Pretoria,很明显是错误的。
Metric definitions – Docs - Umami 文档和 umami/src/lib/detect.ts at master · umami-software/umami 代码上说 IP 地址来自 HTTP Header,但经过那么多 CDN、Proxy 后还能不能留住源 IP 都不好说,Umami 也没个调试功能。
那就部署一个 echo server,httpbin,看看请求过去的 Header 都有哪些。
GET https://httpbin.kokomi.me/headers?show_env=1
HTTP/2.0 200 OK
access-control-allow-credentials: true
access-control-allow-origin: *
content-length: 447
content-type: application/json
date: Tue, 14 Oct 2025 09:40:00 GMT
eo-cache-status: MISS
eo-log-uuid: 17097984847967099199
server: nginx
strict-transport-security: max-age=3600;
{
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip,deflate,br",
"Cdn-Loop": "TencentEdgeOne; loops=2",
"Connection": "close",
"Eo-Connecting-Ip": "2409:8a1e:6941:1d0:361c:744e:1f6e:151d",
"Eo-Log-Uuid": "677971750601829410",
"Host": "httpbin.kokomi.me",
"Remote-Host": "125.94.248.100",
"User-Agent": "xh/0.25.0",
"X-Forwarded-For": "2409:8a1e:6941:1d0:361c:744e:1f6e:151d, 125.94.248.100",
"X-Forwarded-Host": "httpbin.kokomi.me",
"X-Forwarded-Port": "443",
"X-Forwarded-Proto": "https",
"X-Real-Ip": "125.94.248.100",
"X-Real-Port": "63002"
}
}注意请求参数需要添加
show_env=1,才会显示X-Real-Ip这类 header。如果不是看到 Include X-Forwarded-For header in headers section · 议题 #300 · postmanlabs/httpbin 完全没有文档说明。还得是在 从源码翻到的 legacy 落地页上 才有说明。
一阵调试最后发现,HTTP Header 传过来的 IP 都是对的,但因为是 IPv6,尝试删除端口号时做了截断 😑 但官方 SaaS 服务又是没问题的,不好说这里有什么代码版本的鸿沟,还是强制 IPv4 了?
export async function getLocation(ip: string = '', headers: Headers, hasPayloadIP: boolean) {
// Ignore local ips
if (await isLocalhost(ip)) {
return;
}
if (!hasPayloadIP && !process.env.SKIP_LOCATION_HEADERS) {
for (const provider of PROVIDER_HEADERS) {
const countryHeader = headers.get(provider.countryHeader);
if (countryHeader) {
const country = decodeHeader(countryHeader);
const region = decodeHeader(headers.get(provider.regionHeader));
const city = decodeHeader(headers.get(provider.cityHeader));
return {
country,
region: getRegionCode(country, region),
city,
};
}
}
}
// Database lookup
if (!global[MAXMIND]) {
const dir = path.join(process.cwd(), 'geo');
global[MAXMIND] = await maxmind.open(
process.env.GEOLITE_DB_PATH || path.resolve(dir, 'GeoLite2-City.mmdb'),
);
}
// When the client IP is extracted from headers, sometimes the value includes a port
const cleanIp = removePortFromIP(ip);
const result = global[MAXMIND].get(cleanIp);
if (result) {
const country = result.country?.iso_code ?? result?.registered_country?.iso_code;
const region = result.subdivisions?.[0]?.iso_code;
const city = result.city?.names?.en;
return {
country,
region: getRegionCode(country, region),
city,
};
}
}知道根因后搜了一下确实有人提到了,而且 master 分支代码已经更新过了。很惊讶这么久居然没支持 IPv6 ?
- Location statistics broken when tracking IPv6 clients · 议题 #3616 · umami-software/umami
- URGENT! Resolve IPv6 address destruction on GeoIP query by malwarepad · 拉取请求 #3627 · umami-software/umami
想要现在就用起来可以自行编译,或者偷懒一下直接上容器改文件 🙂 sed -E 's/e\?\.split\(":"\)\[0\]/e/' .next/server/chunks/5834.js
宝塔面板产生很多 docker-compose logs 进程
一看 ps auxf 超级多的进程在跑,还有重复的。特别是一重启/升级,主进程结束后,全部都上升给 init 当子进程了,更难处理😑
# ps auxf
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 2844629 0.0 4.6 628420 174280 ? S Sep17 11:56 /www/server/panel/pyenv/bin/python3 /www/server/panel/BT-Panel
root 4083852 0.0 1.1 1949080 44368 ? Sl Oct13 0:04 \_ docker-compose -f /www/dk_project/dk_app/twikoo/twikoo_N82a/docker-compose.yml logs -f --tail 10
root 4083876 0.0 1.1 1875348 44364 ? Sl Oct13 0:04 \_ docker-compose -f /www/dk_project/dk_app/twikoo/twikoo_N82a/docker-compose.yml logs -f --tail 10
root 4086103 0.0 1.1 1875348 44736 ? Sl Oct13 0:04 \_ docker-compose -f /www/dk_project/dk_app/twikoo/twikoo_N82a/docker-compose.yml logs -f --tail 10
root 4086118 0.0 1.1 1875348 44340 ? Sl Oct13 0:04 \_ docker-compose -f /www/dk_project/dk_app/twikoo/twikoo_N82a/docker-compose.yml logs -f --tail 10
root 4101470 0.0 1.1 1875348 44220 ? Sl Oct13 0:04 \_ docker-compose -f /www/dk_project/dk_app/twikoo/twikoo_N82a/docker-compose.yml logs -f --tail 10
root 4101492 0.0 1.1 1875348 44164 ? Sl Oct13 0:04 \_ docker-compose -f /www/dk_project/dk_app/twikoo/twikoo_N82a/docker-compose.yml logs -f --tail 10
root 4126193 0.0 1.2 1875348 45920 ? Sl Oct14 0:08 \_ docker-compose -f /www/dk_project/dk_app/uptime_kuma/uptime_kuma_MLir/docker-compose.yml logs -f --tail 10
root 4126208 0.0 1.2 1875348 45780 ? Sl Oct14 0:08 \_ docker-compose -f /www/dk_project/dk_app/uptime_kuma/uptime_kuma_MLir/docker-compose.yml logs -f --tail 10
root 4126469 0.0 1.2 1875348 45720 ? Sl Oct14 0:08 \_ docker-compose -f /www/dk_project/dk_app/uptime_kuma/uptime_kuma_MLir/docker-compose.yml logs -f --tail 10
....没仔细看代码怎么产生的,但 kill 掉也没发现异常。
DOCKER_COMPOSE_PIDS=$(pgrep -f "docker-compose")
ps -f -p "$DOCKER_COMPOSE_PIDS"
echo "$DOCKER_COMPOSE_PIDS" | xargs kill