需从服务端Nginx日志(用$request_time、专用日志格式、X-Static-Hit等)和客户端Performance API(配Timing-Allow-Origin、采集duration/responseStatus)双维度监控静态资源耗时与错误率,并通过Prometheus/Loki聚合分析。

要监控前端静态资源(如 JS、CSS、图片、字体)在 Nginx 侧的真实加载耗时与错误率,需从服务端可观测性和客户端真实体验两个维度协同入手。Nginx 本身不执行 JS,但它是静态资源的最终交付者——它的响应质量直接决定前端能否顺利加载。
一、服务端:精准捕获 Nginx 处理静态资源的耗时与状态
关键不是看平均值,而是分离路径、用对变量、排除干扰。
使用
$request_time(单位:秒,含毫秒精度)作为核心耗时指标
它从请求头接收完成开始计时,到响应体发送完毕结束,覆盖文件打开、读取、内核发送全过程。静态资源不走 upstream,$upstream_response_time无效或为-,必须依赖它。-
配置专用日志格式,只记录静态请求:
log_format static_log '$remote_addr - [$time_local] "$request" $status $body_bytes_sent $request_time $msec';
-
在匹配静态后缀的
location中启用独立日志并打标:立即学习“前端免费学习笔记(深入)”;
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?|ttf|eot)$ { access_log /var/log/nginx/static_access.log static_log; expires 1h; add_header X-Static-Hit $sent_http_content_length; add_header X-File-Cache $upstream_cache_status; add_header Timing-Allow-Origin "https://your-app.com"; # 启用前端性能采集 }-
X-Static-Hit回传实际字节数,0 表示 304 或空响应,辅助识别缓存有效性 -
X-File-Cache显示HIT/MISS/BYPASS,反映open_file_cache命中情况(注意:直连root时该变量仍有效) -
Timing-Allow-Origin是前端获取真实PerformanceResourceTiming的前提(见下文)
-
错误率 = 状态码非
2xx/304的请求数占比
关注403(权限)、416(范围请求越界)、500(磁盘满/权限异常导致读取中断)、特别是200伴随ERR_CONTENT_LENGTH_MISMATCH(背后常是缓存文件损坏或proxy_temp目录无写权限)
二、客户端:获取浏览器真实的加载耗时与失败原因
服务端日志只能告诉你“我发了什么”,而浏览器能告诉你“用户到底收到了什么”。
必须配置
Timing-Allow-Origin响应头(如上),否则跨域资源的duration、connectStart、fetchStart等字段在performance.getEntriesByType('resource')中均为0或undefined。-
前端可主动采集关键指标:
new PerformanceObserver((list) => { for (const entry of list.getEntries()) { if (entry.initiatorType === 'script' || entry.initiatorType === 'link' || entry.initiatorType === 'img') { console.info(`${entry.name} loaded in ${entry.duration}ms, status: ${entry.responseStatus}`); } } }).observe({ entryTypes: ['resource'] });-
entry.duration:真实加载耗时(含 DNS、TCP、TLS、请求、响应、解析) -
entry.responseStatus:HTTP 状态码(可识别0表示加载失败,如网络中断、CORS 拒绝、ERR_CONTENT_LENGTH_MISMATCH) - 结合
entry.transferSize和entry.encodedBodySize可判断是否命中 CDN 或 Nginx 缓存
-
-
错误率统计建议按资源类型分组:
- JS 加载失败 → 页面逻辑可能中断,优先级最高
- CSS 加载失败 → 渲染阻塞,白屏风险
- 图片/字体失败 → 体验降级,但通常不影响功能
三、聚合分析:把服务端 + 客户端数据打通看
原始日志和前端上报需交由可观测平台统一处理:
-
Prometheus + Grafana(推荐服务端主视角)
- 用
nginx-prometheus-exporter --enable-static-stats暴露nginx_static_request_seconds直方图 - P95 耗时:
histogram_quantile(0.95, sum(rate(nginx_static_request_seconds_bucket[5m])) by (le)) - 错误率:
sum(rate(nginx_static_request_seconds_count{status=~"4..|5.."}[5m])) / sum(rate(nginx_static_request_seconds_count[5m])) - 抖动系数:
stddev_over_time(nginx_static_request_seconds_sum[5m]) / avg_over_time(nginx_static_request_seconds_count[5m]) > 0.3即告警
- 用
-
Loki + Grafana(适合日志驱动排查)
- 提取
$request_time并按后缀分组:avg_over_time({job="nginx"} |~GET.*.(js|css)| regexp"(\d+.\d+)"| unwrap __error__ [5m]) - 查看
499(客户端主动断连)是否集中出现 → 可能是首屏 JS 执行卡死导致用户刷新
- 提取
-
前端埋点 + 后端日志关联(高级)
- 前端在资源加载完成时带上
X-Request-ID上报失败事件 - Nginx 日志中添加
$request_id字段,便于跨系统追踪同一请求全链路
- 前端在资源加载完成时带上
不复杂但容易忽略


















