memoflow-frontend-performance-optimization-server-response-slow
记录一下前端性能优化实战,dailyuse(memoflow) web 端 的 服务端响应慢的诊断
[!info] related notes
- 前置笔记:
- 相关 MOC:
memoflow-frontend-performance-optimization-server-response-slow
下面是一张登录页直接刷新后的 network 截图。

结论先说:
这次卡很久的主因,不在浏览器,也不主要在前端 bundle 下载,而是在服务端返回太慢。
具体说,这个auth?redirect=/请求,大部分时间都耗在Waiting for server response,也就是 TTFB 很高。
图里的关键信息
当前选中的请求是:
auth?redirect=/
它的 Timing 大概是:
- Queueing:
0.62 ms - Stalled:
0.73 ms - DNS Lookup:
4.54 ms - Initial connection:
1.66 s - SSL:
1.66 s - Request sent:
0.30 ms - Waiting for server response:
33.12 s - Content Download:
28.00 ms - 总耗时:
34.81 s
这组数据特别关键,因为它告诉我们:
1. 不是浏览器在排队
Queueing 和 Stalled 都非常短,几乎可以忽略。
也就是说,不是“Chrome 卡着没发”,也不是连接数占满导致长时间排队。
2. 不是下载慢
Content Download 只有 28 ms。
这说明一旦服务器真正开始返回内容,浏览器几乎是瞬间就下完了。
所以绝对不是“大文件下载太慢”这种问题。
3. 真正慢的是服务器“迟迟不开始回”
最核心的是这句:
- Waiting for server response:33.12 s
这就是典型的:
请求很快发出去了,但服务器 33 秒后才开始吐出第一个字节。
这类问题本质上就是:
- 后端处理慢
- 上游依赖慢
- 鉴权链路慢
- 数据库慢
- 外部服务慢
- 反向代理把请求挂住了
而不是前端渲染慢。
这张图里还有一个特别有价值的信息:Server Timing
你下面还能看到:
cfCacheStatus — DYN…cfEdge — 6.00 mscfOrigin — 31.47 s
这几个字段非常有用。
cfEdge = 6 ms
说明 Cloudflare 边缘节点处理得很快。
也就是:
- 不是 CDN 边缘节点慢
- 不是离你近的接入层慢
cfOrigin = 31.47 s
这几乎已经直接告诉你:
真正慢的是源站(origin)
也就是 Cloudflare 往你后端源站请求时,源站花了 31 秒多才给结果。
cfCacheStatus = DYNAMIC
说明这类响应大概率是动态内容,不是 CDN 缓存直接命中的静态资源。
这和 auth?redirect=/ 这个名字也很吻合。
因为认证相关页面/接口通常就是动态的,不能随便缓存。
所以这次的瓶颈,基本可以定位到哪一层
按概率排序:
第一嫌疑:鉴权/登录态检查慢
因为请求名字就是:
auth?redirect=/
这很像:
- 先检查登录态
- 查 session / token
- 校验 cookie
- 调认证服务
- 再决定重定向到
/
如果这个过程里依赖了:
- 数据库
- Redis
- 第三方认证
- 内部 SSO
- 用户信息服务
任何一个慢,都可能让整个页面白屏等 30 多秒。
第二嫌疑:源站应用逻辑慢
比如这个 auth 路由背后做了很多事:
- 初始化用户上下文
- 拉用户配置
- 查权限
- 查 workspace
- 生成重定向信息
这类逻辑如果写在首个入口请求里,就会非常影响首屏。
第三嫌疑:源站到上游的某个依赖慢
因为 cfOrigin 长,并不一定意味着“Node/PHP/Java 代码本身执行慢”,也可能是它在等别的东西:
- 等数据库
- 等 OAuth
- 等内网 API
- 等对象存储
- 等 DNS
- 等某个 RPC
这和“Chrome 有没有 bug”关系不大
从这张图看,浏览器已经把锅洗得很干净了。
因为现在证据非常明确:
- DNS 很快
- 队列很短
- 请求发送很快
- 下载很快
- 只有“等服务端开始响应”特别久
所以这次不是 Chrome Network 面板假象,也不是单纯 DevTools 抽风。
为什么会白屏
这个也能更准确地解释了:
如果你的站是 SPA 或半 SPA,那么首屏流程可能是:
- 先访问
auth?redirect=/ - 服务端判断你有没有登录
- 决定给你页面、重定向、或返回某种初始化结果
- 前端再继续加载和渲染
如果第 2 步卡了 33 秒,那么用户看到的就是:
- 页面打开了
- 但是内容没出来
- 一直白屏或空壳
所以这类白屏,不是“浏览器画不出来”,而是:
页面在等认证入口返回,导致真正渲染开始得太晚。
那些 JS 请求怎么看
你左侧已经看到很多:
index-DKsCZQVR.jsauth-Bqij9GhvW.jsindex-CZIAToAO.js- 很多
.vue_vue_type_script_setup_…
这说明前端资源后面确实在继续加载。
但当前这张图里,真正最异常的不是这些资源,而是最上面的 auth 请求本身太慢。
也就是说,现在不要被某个 JS 文件分散注意力了。
主矛盾已经变成:auth 入口响应太慢。
这类问题,后端最该查什么
如果你能看服务端日志,建议直接查这条链路:
1. 查 auth?redirect=/ 这条请求在服务端日志里的耗时
重点看:
- 总耗时
- 是不是稳定在 30 秒左右
- 有没有超时重试
- 有没有外部依赖调用
如果它稳定接近 30s,很像某个上游默认超时。
2. 查认证相关依赖
比如:
- session 存储(Redis)
- 用户表查询
- SSO / OAuth 回源
- 权限服务
- 用户资料服务
因为“auth”这种路由,最容易被这些依赖拖慢。
3. 查反向代理 / 网关 / 应用日志是否有 30 秒量级等待
很多系统里会出现:
- upstream timeout
- upstream connect timeout
- upstream response timeout
- 某个内网接口 30 秒才回
如果你日志里刚好也看到 30~31 秒,就和这张图完全对上了。
你现在在 DevTools 里还能继续看什么
看 Headers
重点看:
- Status Code
- 是否 302 / 200
- Response Headers
server-timinglocation头
如果这是 auth 跳转流程,状态码和 location 很关键。
看 Response
看它最后返回的是:
- HTML
- JSON
- 重定向页
- 错误信息
有时候虽然状态码 200,但返回体其实是登录页、错误页或降级页。
只筛 Doc
你现在最该优先看 document 请求链,而不是先盯 JS。
因为现在最大的证据就是:
document / auth 入口本身慢
一句话总结这次刷新结果
这次图已经很清楚了:
卡顿的核心不是浏览器,不是下载,不是前端 bundle 解析,而是
auth?redirect=/这个请求的服务端首字节响应太慢。
更具体一点:
- 浏览器排队:不是问题
- 网络下载:不是问题
- CDN 边缘:不是问题
- 源站处理 / 鉴权链路:高度可疑
而白屏,很可能就是因为:
页面在等 auth 入口返回,首屏渲染被整个卡住了。
探查1(服务器到容器 or 容器内容问题)
root@iZ2vcc4gi9ajfcwotk8z1yZ:~# docker stats --no-stream
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
d7ec34980d6a dailyuse-caddy-1 0.00% 24.21MiB / 1.638GiB 1.44% 28.2MB / 127MB 51MB / 69.6kB 9
b4c79a415e1e dailyuse-api-1 6.91% 243.9MiB / 1.638GiB 14.54% 244kB / 63.5kB 176MB / 4.34MB 21
cd583404acd8 dailyuse-powersync-1 0.00% 145.2MiB / 1.638GiB 8.66% 47.9MB / 19.2MB 194MB / 103MB 11
4204d6e5f6e3 dailyuse-web-1 0.00% 4.188MiB / 1.638GiB 0.25% 208kB / 1.49MB 12.5MB / 4.1kB 3
4e0002a3032c dailyuse-postgres-1 0.02% 58.16MiB / 1.638GiB 3.47% 18.2MB / 47.4MB 136MB / 102MB 10
e4a6a44d5469 dailyuse-ai-service-1 0.13% 58.79MiB / 1.638GiB 3.51% 45.1kB / 786B 279MB / 0B 7
79e01737b876 dailyuse-watchtower-1 0.00% 12.91MiB / 1.638GiB 0.77% 138MB / 31.6MB 106MB / 0B 9
d040e1f62c26 dailyuse-redis-1 0.53% 9.188MiB / 1.638GiB 0.55% 149MB / 105MB 209MB / 0B 6
root@iZ2vcc4gi9ajfcwotk8z1yZ:~# curl -k --resolve dailyuse.bakersean.top:443:127.0.0.1 \
-o /dev/null -s \
-w 'ttfb=%{time_starttransfer} total=%{time_total}\n' \
https://dailyuse.bakersean.top/auth?redirect=/
ttfb=0.006812 total=0.055080
root@iZ2vcc4gi9ajfcwotk8z1yZ:~# docker exec -it dailyuse-web-1 sh -lc "curl -s -o /dev/null -w 'ttfb=%{time_starttransfer} total=%{time_total}\n' http://127.0.0.1:80/auth?redirect=/"
ttfb=0.000940 total=0.001120
- docker exec 里直打 127.0.0.1:80/auth?redirect=/ 只有 1ms,说明应用容器本身不慢。
- 宿主机上 —resolve …:127.0.0.1 走 HTTPS 也只有 55ms,说明本机反代/证书/路由也正常。
探查2(公网链路、cookie)
在服务器内部额外的走一层,打到目标地址来测试源站侧链路是否存在问题。
通常是在测:
- 这台机器的公网入口是否正常
- 443 监听是否正常
- Caddy/Nginx 这一层是否正常
- 证书/SNI/虚拟主机路由是否正常
root@iZ2vcc4gi9ajfcwotk8z1yZ:~# curl 'https://dailyuse.bakersean.top/auth?redirect=/' \
-H 'accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7' \
-H 'accept-language: zh-CN,zh;q=0.9' \
-H 'cache-control: max-age=0' \
-H 'priority: u=0, i' \
-H 'sec-ch-ua: "Chromium";v="146", "Not-A.Brand";v="24", "Google Chrome";v="146"' \
-H 'sec-ch-ua-mobile: ?0' \
-H 'sec-ch-ua-platform: "Windows"' \
-H 'sec-fetch-dest: document' \
-H 'sec-fetch-mode: navigate' \
-H 'sec-fetch-site: same-origin' \
-H 'sec-fetch-user: ?1' \
-H 'upgrade-insecure-requests: 1' \
-H 'user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36'
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="theme-color" content="#181A1F" />
<meta name="description" content="知行(Memoflow),将知识转化为行动的流式个人工作台。" />
<link rel="icon" href="/favicon.ico" sizes="any" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<link rel="manifest" href="/site.webmanifest" />
<title>知行 Memoflow</title>
<script type="module" crossorigin src="/assets/index-DKsCZQVR.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DyqXXKgJ.css">
</head>
<body>
<div id="app"></div>
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v8c78df7c7c0f484497ecbca7046644da1771523124516" integrity="sha512-8DS7rgIrAmghBFwoOTujcf6D9rXvH8xm8JQ1Ja01h9QX8EzXldiszufYa4IFfKdLUKTTrnSFXLDkUEOTrZQ8Qg==" data-cf-beacon='{"version":"2024.11.0","token":"b6a27aa214624012ae1355bad07fa857","r":1,"server_timing":{"name":{"cfCacheStatus":true,"cfEdge":true,"cfExtPri":true,"cfL4":true,"cfOrigin":true,"cfSpeedBrain":true},"location_startswith":null}}' crossorigin="anonymous"></script>
</body>
</html>
root@iZ2vcc4gi9ajfcwotk8z1yZ:~# curl 'https://dailyuse.bakersean.top/auth?redirect=/' \
-H 'accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7' \
-H 'accept-language: zh-CN,zh;q=0.9' \
-H 'cache-control: max-age=0' \
-H 'priority: u=0, i' \
-H 'sec-ch-ua: "Chromium";v="146", "Not-A.Brand";v="24", "Google Chrome";v="146"' \
-H 'sec-ch-ua-mobile: ?0' \
-H 'sec-ch-ua-platform: "Windows"' \
-H 'sec-fetch-dest: document' \
-H 'sec-fetch-mode: navigate' \
-H 'sec-fetch-site: same-origin' \
-H 'sec-fetch-user: ?1' \
-H 'upgrade-insecure-requests: 1' \
-H 'user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36' \
-o /dev/null -sS \
-w $'\ncode=%{http_code}\nremote_ip=%{remote_ip}\nhttp_version=%{http_version}\ndns=%{time_namelookup}s\nconnect=%{time_connect}s\ntls=%{time_appconnect}s\npretransfer=%{time_pretransfer}s\nttfb=%{time_starttransfer}s\ntotal=%{time_total}s\nredirect=%{time_redirect}s\nsize=%{size_download}B\nspeed=%{speed_download}B/s\n'
code=200
remote_ip=104.21.45.239
http_version=2
dns=0.226352s
connect=0.434842s
tls=0.652395s
pretransfer=0.652558s
ttfb=1.057069s
total=1.057152s
redirect=0.000000s
size=1409B
speed=1332B/s
root@iZ2vcc4gi9ajfcwotk8z1yZ:~# curl -k --resolve dailyuse.bakersean.top:443:47.108.252.228 -o /dev/null -s -w 'ttfb=%{time_starttransfer} total=%{time_total}\n' https://dailyuse.bakersean.top/auth?redirect=/
ttfb=0.009024 total=0.009423
root@iZ2vcc4gi9ajfcwotk8z1yZ:~# curl --http1.1 'https://dailyuse.bakersean.top/auth?redirect=/' \
-o /dev/null -sS \
-w $'h1 code=%{http_code} ttfb=%{time_starttransfer}s total=%{time_total}s\n'
h1 code=200 ttfb=1.382599s total=1.382697s
root@iZ2vcc4gi9ajfcwotk8z1yZ:~# curl --http2 'https://dailyuse.bakersean.top/auth?redirect=/' \
-o /dev/null -sS \
-w $'h2 code=%{http_code} ttfb=%{time_starttransfer}s total=%{time_total}s\n'、
h2 code=200 ttfb=1.361289s total=1.361378s
、root@iZ2vcc4gi9ajfcwotk8z1yZ:~#
探查3 代理
关闭代理客户端后,等待服务器的时间明显降低
但是我实际上已经配置了域名是 DIRECT,不应该是节点的问题。

本地代理客户端的接管方式,仍然很可能在影响这条链路。
尤其是:
- DNS
- Fake-IP
- QUIC/UDP
- TUN 准备查看开关代理时的 协议是否变化,结果突然 开启代理时也恢复正常(只有1s多加载时长)了
[!notice] 注意 这是未解之谜,为毛突然变快了,关闭了缓存,甚至跑进了1s

发现注册成功跳转到主应用时存在较长时间的白屏