memoflow-frontend-performance-optimization-server-response-slow

记录一下前端性能优化实战,dailyuse(memoflow) web 端 的 服务端响应慢的诊断

#type / howto #status / growing

[!info] related notes

  • 前置笔记:
  • 相关 MOC:

memoflow-frontend-performance-optimization-server-response-slow

下面是一张登录页直接刷新后的 network 截图。

Pasted image 20260413165042

结论先说:

这次卡很久的主因,不在浏览器,也不主要在前端 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 ms
  • cfOrigin — 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,那么首屏流程可能是:

  1. 先访问 auth?redirect=/
  2. 服务端判断你有没有登录
  3. 决定给你页面、重定向、或返回某种初始化结果
  4. 前端再继续加载和渲染

如果第 2 步卡了 33 秒,那么用户看到的就是:

  • 页面打开了
  • 但是内容没出来
  • 一直白屏或空壳

所以这类白屏,不是“浏览器画不出来”,而是:

页面在等认证入口返回,导致真正渲染开始得太晚。


那些 JS 请求怎么看

你左侧已经看到很多:

  • index-DKsCZQVR.js
  • auth-Bqij9GhvW.js
  • index-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-timing
  • location

如果这是 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,不应该是节点的问题。 Pasted image 20260413185113

本地代理客户端的接管方式,仍然很可能在影响这条链路。

尤其是:

  • DNS
  • Fake-IP
  • QUIC/UDP
  • TUN 准备查看开关代理时的 协议是否变化,结果突然 开启代理时也恢复正常(只有1s多加载时长)了

[!notice] 注意 这是未解之谜,为毛突然变快了,关闭了缓存,甚至跑进了1s

Pasted image 20260413191635

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

创建于 2026/4/13 更新于 2026/5/27