Karakeep 内网访问与中文截图问题排查记录
记录时间:2026-09-02
环境:Ubuntu / Docker Compose / Karakeep / OpenResty / v2rayN
内网服务器:192.168.5.100
内网域名:*.home.52huahua.cn
今天主要折腾了一下 Karakeep。
原本只是想把家里的一些内网页面收藏到 Karakeep,结果一路遇到了几个问题:
- 浏览器访问 Karakeep 出现 502
- Karakeep 无法抓取内网域名
- Crawler 提示禁止访问
192.168.5.100 - 放行以后又出现
ERR_NAME_NOT_RESOLVED - 服务器 DNS 和 IPv6 配置存在问题
- 最后页面虽然能抓取了,但 Karakeep 生成的截图中文全部变成方框
这几个问题实际上来自不同层级,最后分别定位到了 v2rayN、Karakeep SSRF 防护、服务器 DNS、IPv6 配置和 Chrome 容器字体。
1. 浏览器访问 Karakeep 出现 502
Karakeep 部署在:
192.168.5.100:3000
通过 OpenResty 使用:
karakeep.home.52huahua.cn
进行反向代理。
一开始浏览器访问时直接出现:
502 Bad Gateway
先在服务器端测试 Karakeep:
curl -I http://192.168.5.100:3000
返回:
HTTP/1.1 307 Temporary Redirect
location: /signin
X-Powered-By: Next.js
说明 Karakeep 本身正常。
继续测试 OpenResty:
curl -I http://karakeep.home.52huahua.cn/signin
同样可以正常返回:
HTTP/1.1 200 OK
Server: openresty
X-Powered-By: Next.js
到这里基本可以排除 Karakeep 和 OpenResty 本身的问题。
后来在浏览器开发者工具里发现一个很关键的信息:
Remote Address: 127.0.0.1:10808
这个端口正好是本机 v2rayN 的代理端口。
也就是说浏览器实际上走的是:
浏览器
↓
v2rayN / Xray
↓
karakeep.home.52huahua.cn
而不是直接访问局域网。
Xray 日志也能看到:
using outbound/direct[direct]
lookup karakeep.home.52huahua.cn:
(exchange4: NXDOMAIN | exchange6: NXDOMAIN)
这里说明 direct 路由规则其实已经匹配成功。
真正的问题是 Xray 自己解析内网域名失败。
所以这个 502 并不是 OpenResty 或 Karakeep 返回的,而是本机代理链路导致的。
2. Karakeep 无法收藏内网页面
浏览器访问解决以后,又发现 Karakeep 无法正常抓取:
https://new.home.52huahua.cn/
先进入 Karakeep 容器测试 DNS:
getent hosts new.home.52huahua.cn
可以正确得到:
192.168.5.100 new.home.52huahua.cn
继续测试:
curl -vI https://new.home.52huahua.cn/
HTTPS、证书、OpenResty 全部正常:
Connected to 192.168.5.100:443
SSL certificate verify ok.
HTTP/2 200
server: openresty
因此可以确认:
DNS 正常
TCP 正常
HTTPS 正常
SSL 证书 正常
OpenResty 正常
目标网站 正常
这时候继续查看 Karakeep Crawler 日志,终于找到了真正原因:
Refusing to access disallowed resolved address 192.168.5.100
for host new.home.52huahua.cn
同时还有:
Disallowed navigation target
Karakeep 默认存在 SSRF 防护,不允许 Crawler 请求解析到私有 IP 的地址。
我的域名:
new.home.52huahua.cn
↓
192.168.5.100
正好属于私有网络,因此被 Karakeep 主动拦截。
3. 放行 Karakeep 内网域名
解决方法是在 Karakeep 环境变量中加入:
CRAWLER_ALLOWED_INTERNAL_HOSTNAMES=.home.52huahua.cn
这里使用:
.home.52huahua.cn
表示允许该域名下的内部子域。
例如:
new.home.52huahua.cn
karakeep.home.52huahua.cn
emby.home.52huahua.cn
xxx.home.52huahua.cn
重新创建服务以后检查:
docker exec karakeep-web-1 env | grep CRAWLER_ALLOWED_INTERNAL_HOSTNAMES
返回:
CRAWLER_ALLOWED_INTERNAL_HOSTNAMES=.home.52huahua.cn
说明配置已经正确进入 Karakeep 容器。
4. SSRF 解决以后,又出现 DNS 解析失败
比较有意思的是,加完 CRAWLER_ALLOWED_INTERNAL_HOSTNAMES 以后,错误发生了变化。
原来是:
Refusing to access disallowed resolved address
后来变成:
net::ERR_NAME_NOT_RESOLVED
以及:
getaddrinfo ENOTFOUND new.home.52huahua.cn
这个变化其实是个好现象。
说明 Karakeep 已经不再因为 SSRF 防护拒绝这个域名了,现在真正卡在 DNS。
于是继续检查服务器:
resolvectl dns
发现:
Link 2 (eno1): 114.114.114.114 fe80::5
再查看:
cat /etc/resolv.conf
也是:
nameserver 114.114.114.114
nameserver fe80::5%2
search .
问题就出在这里。
114.114.114.114 是公网 DNS,它并不知道家庭网络里的:
*.home.52huahua.cn
因此内网域名解析自然失败。
5. 发现服务器 DNS 被 Netplan 写死
继续检查:
networkctl status eno1
确认当前网络由:
Netplan
+
systemd-networkd
管理。
查看 Netplan:
cat /etc/netplan/*.yaml
原来的配置类似:
network:
ethernets:
eno1:
addresses:
- 192.168.5.100/24
dhcp6: true
match:
macaddress: b4:2e:99:63:ce:57
nameservers:
addresses:
- 114.114.114.114
search: []
routes:
- to: default
via: 192.168.5.1
set-name: eno1
version: 2
也就是说,这台服务器不仅 IPv4 地址是静态配置,DNS 同样也是静态写死的。
所以即使路由器 DHCP 下发的 DNS 已经修改,这台服务器也不会跟着变化。
6. 修改服务器 DNS
最终把 Netplan 调整为:
network:
ethernets:
eno1:
addresses:
- 192.168.5.100/24
dhcp6: false
accept-ra: false
link-local: []
match:
macaddress: b4:2e:99:63:ce:57
nameservers:
addresses:
- 192.168.5.100
- 192.168.5.1
search: []
routes:
- to: default
via: 192.168.5.1
set-name: eno1
version: 2
其中:
192.168.5.100
是服务器本机使用的内部 DNS。
192.168.5.1
作为家庭网络的备用 DNS。
修改完成后重新应用 Netplan 配置。
7. 顺便彻底关闭 IPv6
处理 DNS 的时候还发现:
fe80::5
一直出现在服务器 DNS 配置中。
目前这台服务器并不需要 IPv6,所以顺便彻底关闭。
Netplan 中加入:
dhcp6: false
accept-ra: false
link-local: []
同时创建:
/etc/sysctl.d/99-disable-ipv6.conf
内容:
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.eno1.disable_ipv6 = 1
应用:
sysctl --system
检查:
cat /proc/sys/net/ipv6/conf/all/disable_ipv6
cat /proc/sys/net/ipv6/conf/eno1/disable_ipv6
都应该返回:
1
最后检查:
ip -6 addr show dev eno1
已经没有 IPv6 地址。
这里还踩了一个小坑。
仅仅配置:
dhcp6: false
并不代表彻底关闭 IPv6。
之前生成的 systemd-networkd 配置里仍然存在:
LinkLocalAddressing=ipv6
也就是说 IPv6 Link-local 地址仍然会被创建。
因此还需要:
accept-ra: false
link-local: []
才能把这一层也关掉。
8. Karakeep 能抓取了,但截图中文全部变成方框
前面的网络问题全部解决以后,Karakeep 已经可以正常收藏:
https://new.home.52huahua.cn/
结果又遇到了最后一个问题:
Karakeep 自动生成的网页截图中,中文全部显示成方框。
但是:
- 英文正常
- 数字正常
- 图片正常
- 页面布局正常
所以基本可以排除 UTF-8 或网页编码问题。
Karakeep 的截图实际上走的是:
Karakeep Worker
↓
Playwright
↓
karakeep-chrome
↓
Headless Chromium
↓
网页渲染
↓
生成截图
所以 Windows 浏览器能正常显示中文没有任何意义。
真正需要中文字体的是:
karakeep-chrome
这个 Docker 容器。
9. 检查 Karakeep Chrome 镜像
当前官方镜像:
ghcr.io/karakeep-app/karakeep-chrome:release
检查系统:
docker exec karakeep-chrome-1 sh -c \
'cat /etc/os-release; echo "---"; command -v apt-get; command -v apk; command -v fc-cache'
得到:
PRETTY_NAME="Debian GNU/Linux 13 (trixie)"
VERSION_ID="13"
VERSION="13 (trixie)"
VERSION_CODENAME=trixie
并且存在:
/usr/bin/apt-get
说明当前 Karakeep Chrome 官方镜像基于 Debian 13。
但是最开始:
fc-list
都无法使用。
说明官方 Chrome 镜像中没有完整安装 fontconfig 和中文字体。
10. 自定义 karakeep-chrome 镜像
决定直接基于官方 Chrome 镜像增加中文字体。
目录结构:
/opt/karakeep/
├── docker-compose.yml
├── .env
└── chrome/
└── Dockerfile
Dockerfile:
FROM ghcr.io/karakeep-app/karakeep-chrome:release
USER root
RUN printf '%s\n' \
'Types: deb' \
'URIs: http://mirrors.aliyun.com/debian' \
'Suites: trixie trixie-updates' \
'Components: main' \
'Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg' \
'' \
'Types: deb' \
'URIs: http://mirrors.aliyun.com/debian-security' \
'Suites: trixie-security' \
'Components: main' \
'Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg' \
> /etc/apt/sources.list.d/debian.sources
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
fontconfig \
fonts-noto-cjk \
fonts-noto-color-emoji \
&& fc-cache -f \
&& rm -rf /var/lib/apt/lists/*
这里主要安装:
fontconfig
fonts-noto-cjk
fonts-noto-color-emoji
其中真正解决中文缺字问题的是:
fonts-noto-cjk
它提供完整的中日韩字体支持。
11. Debian 国内镜像源又踩了一个坑
最开始为了提高下载速度,使用了清华 Debian 镜像:
https://mirrors.tuna.tsinghua.edu.cn/debian
结果构建时出现:
SSL connection failed:
certificate verify failed
随后又出现:
E: Unable to locate package fontconfig
E: Unable to locate package fonts-noto-cjk
E: Unable to locate package fonts-noto-color-emoji
这里后面的:
Unable to locate package
比较容易误导。
实际上并不是 Debian 13 没有这些包。
真正的问题是前面的:
apt-get update
因为 HTTPS Certificate Verify Failed,没有正常下载软件包索引。
没有 package index 后,apt-get install 自然找不到这些软件包。
最后改成阿里云 HTTP:
http://mirrors.aliyun.com/debian
http://mirrors.aliyun.com/debian-security
成功完成安装。
12. 修改 Docker Compose
原来的 Chrome 服务直接使用官方镜像:
chrome:
image: ghcr.io/karakeep-app/karakeep-chrome:release
改成:
chrome:
build:
context: ./chrome
command:
- --disable-gpu
- --disable-dev-shm-usage
- --hide-scrollbars
- --disable-blink-features=AutomationControlled
- --window-size=1440,900
init: true
restart: unless-stopped
检查最终 Compose 配置:
docker compose config | sed -n '/chrome:/,/^[^ ]/p'
能够看到:
chrome:
build:
context: /opt/karakeep/chrome
dockerfile: Dockerfile
说明 Compose 已经正确读取自定义 Build 配置。
13. 最后一个坑:镜像 Build 成功,不代表容器已经使用新镜像
执行:
docker compose build chrome
构建已经成功:
naming to docker.io/library/karakeep-chrome:latest
但是 Karakeep 截图仍然是乱码。
于是检查:
docker exec karakeep-chrome-1 fc-match "sans-serif:lang=zh-cn"
却得到:
exec: "fc-match": executable file not found in $PATH
这个结果明显不对。
因为 Dockerfile 已经安装:
fontconfig
只要运行的是新镜像,fc-match 就应该存在。
于是继续检查当前容器:
docker inspect karakeep-chrome-1 \
--format 'Image={{.Config.Image}}'
结果:
Image=ghcr.io/karakeep-app/karakeep-chrome:release
再看:
docker compose images
发现运行中的 Chrome 仍然是:
karakeep-chrome-1
ghcr.io/karakeep-app/karakeep-chrome
release
150MB
这时候才发现:
新的 Docker 镜像确实已经 Build 成功,但是正在运行的 Container 根本没有重新创建。
这也是今天最后一个、同时也是最容易忽略的问题。
14. 强制重新创建 Chrome 容器
不需要把整个 Karakeep 全部停掉,只重新创建 Chrome:
docker compose up -d --force-recreate --no-deps chrome
其中:
--force-recreate
强制重新创建 Chrome Container。
而:
--no-deps
避免影响:
karakeep-web
karakeep-meilisearch
重新创建后检查:
docker compose images
终于变成:
karakeep-chrome-1
karakeep-chrome
latest
237MB
之前官方镜像只有:
150MB
现在自定义镜像:
237MB
说明包含 Noto CJK 字体的新镜像终于真正运行起来了。
重新让 Karakeep 抓取页面以后:
中文截图恢复正常。
问题到这里彻底解决。
最终保留配置
Karakeep 内网 Crawler
.env:
CRAWLER_ALLOWED_INTERNAL_HOSTNAMES=.home.52huahua.cn
Chrome Docker Compose
chrome:
build:
context: ./chrome
command:
- --disable-gpu
- --disable-dev-shm-usage
- --hide-scrollbars
- --disable-blink-features=AutomationControlled
- --window-size=1440,900
init: true
restart: unless-stopped
Chrome Dockerfile
FROM ghcr.io/karakeep-app/karakeep-chrome:release
USER root
RUN printf '%s\n' \
'Types: deb' \
'URIs: http://mirrors.aliyun.com/debian' \
'Suites: trixie trixie-updates' \
'Components: main' \
'Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg' \
'' \
'Types: deb' \
'URIs: http://mirrors.aliyun.com/debian-security' \
'Suites: trixie-security' \
'Components: main' \
'Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg' \
> /etc/apt/sources.list.d/debian.sources
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
fontconfig \
fonts-noto-cjk \
fonts-noto-color-emoji \
&& fc-cache -f \
&& rm -rf /var/lib/apt/lists/*
以后更新 Karakeep
由于现在 karakeep-chrome 是基于官方镜像二次构建的,所以以后官方 Chrome 镜像更新后,需要重新 Build。
可以执行:
docker compose pull
docker compose build --pull chrome
docker compose up -d --force-recreate --no-deps chrome
然后确认:
docker compose images
再验证中文字体:
docker exec karakeep-chrome-1 \
fc-match "sans-serif:lang=zh-cn"
正常应该能够匹配到 Noto CJK 字体。
今天踩到的几个坑
今天这一轮折腾下来,主要记住几个点:
-
浏览器能访问内网页面,不代表 Docker Crawler 也能访问,二者 DNS 和网络环境可能完全不同。
-
Karakeep 默认会阻止 Crawler 访问私有 IP。家庭内网页面需要配置:
CRAWLER_ALLOWED_INTERNAL_HOSTNAMES=.home.52huahua.cn -
错误从:
Disallowed resolved address变成:
ERR_NAME_NOT_RESOLVED不代表配置没有生效,反而说明已经成功通过了上一层 SSRF 检查。
-
Netplan 里写死 DNS 后,修改路由器 DHCP DNS 对服务器没有作用。
-
dhcp6: false不等于彻底关闭 IPv6,IPv6 Link-local 仍然可能存在。 -
Headless Chrome 截图中文显示成方框,优先检查 Chrome 容器有没有 CJK 字体。
-
apt提示:Unable to locate package不一定真的是包不存在,要往前看
apt-get update有没有失败。 -
docker compose build只负责构建镜像。它不代表正在运行的 Container 已经切换到了新镜像。
这个尤其值得记住。
以后修改自定义 Chrome Dockerfile 后:
docker compose build chrome
docker compose up -d --force-recreate --no-deps chrome
然后一定再看:
docker compose images
确认实际运行的镜像。
最终结果
现在整个链路已经正常:
内网 DNS
↓
*.home.52huahua.cn
↓
192.168.5.100
↓
OpenResty
↓
Karakeep Crawler
↓
CRAWLER_ALLOWED_INTERNAL_HOSTNAMES
↓
Headless Chromium
↓
Noto CJK
↓
网页抓取 + 中文截图正常
今天本来只是想解决一个 Karakeep 收藏内网页面的问题,最后把内网 DNS、IPv6、Karakeep SSRF 和 Headless Chrome 中文字体几个问题一起清掉了。
最大的经验还是:
遇到这种链路型问题,不要只盯着最终的 502、ERR_NAME_NOT_RESOLVED 或“乱码”。顺着 Browser → Proxy → DNS → Reverse Proxy → Application → Crawler → Headless Browser 一层一层验证,问题会清晰很多。