Skip to content

Fix management panel responses to include Content-Length - #292

Merged
seakee merged 1 commit into
seakee:mainfrom
kcocoa:fix/management-html-serving
Jul 6, 2026
Merged

seakee merged 1 commit into
seakee:mainfrom
kcocoa:fix/management-html-serving

Conversation

@kcocoa

@kcocoa kcocoa commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR changes the manager panel HTML response to use static-file style response semantics instead of streaming the file with a bare io.Copy.

For PANEL_PATH-backed panels, the server now uses http.ServeContent, which provides Content-Length, Accept-Ranges, Last-Modified, and proper handling for HEAD / range / conditional requests.

For the embedded panel fallback, the server now sets Content-Length explicitly before writing the embedded HTML bytes.

Scope

  • Frontend panel
  • Manager Server
  • CPA panel mode
  • Full Docker mode
  • Native packages / release
  • Docs / Wiki
  • CI / build / tooling

Changes

  • Serve PANEL_PATH management panel HTML with http.ServeContent instead of manual streaming.
  • Set explicit Content-Length for embedded management panel responses.
  • Add compatibility tests covering Content-Length for both embedded and PANEL_PATH panel responses.

User Impact

Users loading /management.html through nginx/reverse proxies should get a more stable panel download, especially for large single-file panel builds.

This does not change the panel UI or management API behavior.

Compatibility / Runtime Notes

  • CPA panel mode: improves PANEL_PATH-backed /management.html serving by making it behave like a normal static file response.
  • Manager Server mode: embedded panel fallback now includes an explicit Content-Length.
  • Full Docker / native packages: no config or packaging changes required.

Data / Security Notes

N/A. This PR only changes HTTP response semantics for the management panel HTML.

Risk / Rollback

Risk level: Low

Rollback notes:

  • Revert this commit to restore the previous manual io.Copy response behavior.

Verification

  • Type check
  • Lint
  • Tests
  • Build
  • Manual UI check
  • Docs/link check
  • Not applicable, docs-only

Commands / evidence:

go test ./...
go test ./internal/service/panel ./internal/httpapi
npm run build

Build output confirms the panel is a large single-file HTML asset:

dist/index.html  4,541.63 kB │ gzip: 1,240.28 kB

Production evidence before the fix showed the upstream app serving /management.html as a chunked response:

HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked

Nginx access logs showed multiple partial-size 200 responses while proxying the old chunked route:

****:****::**** - - [04/Jul/2026:13:46:14 +0000] "GET /management.html HTTP/1.1" 200 3702962 "-" "curl/8.21.0" "-"
****:****::**** - - [04/Jul/2026:13:47:01 +0000] "GET /management.html HTTP/1.1" 200 3882724 "-" "curl/8.21.0" "-"
****:****::**** - - [04/Jul/2026:13:53:29 +0000] "GET /__codex_mgmt_proxy_probe_20260704.html HTTP/2.0" 200 3604348 "-" "curl/8.21.0" "-"
****:****::**** - - [04/Jul/2026:13:53:30 +0000] "GET /__codex_mgmt_proxy_probe_20260704.html HTTP/1.1" 200 3751642 "-" "curl/8.21.0" "-"
****:****::**** - - [04/Jul/2026:13:53:57 +0000] "GET /__codex_mgmt_proxy_probe_20260704.html HTTP/1.1" 200 3636954 "-" "curl/8.21.0" "-"

Nginx also repeatedly buffered the old upstream response to temporary files:

2026/07/04 13:41:34 [warn] 2279961#2279961: *47899 an upstream response is buffered to a temporary file /var/lib/nginx/proxy/7/58/0000007587 while reading upstream, client: ****:****::****, server: ****.********.***, request: "GET /management.html HTTP/2.0", upstream: "http://127.0.0.1:18317/management.html", host: "****.********.***"
2026/07/04 13:43:51 [warn] 2314986#2314986: *47925 an upstream response is buffered to a temporary file /var/lib/nginx/proxy/3/60/0000007603 while reading upstream, client: ****:****::****, server: ****.********.***, request: "GET /management.html HTTP/1.1", upstream: "http://127.0.0.1:18317/management.html", host: "****.********.***"

Local verification after the fix returned stable static-file style headers:

HTTP/1.1 200 OK
Accept-Ranges: bytes
Content-Length: 4541639
Content-Type: text/html; charset=utf-8
Last-Modified: Sat, 04 Jul 2026 14:27:01 GMT

Repeated local downloads returned the full expected size.

Screenshots / Recordings

N/A. Backend response behavior change only; no visible UI changes.

Docs

  • README updated
  • Wiki updated
  • Release notes needed
  • Not needed

Related

N/A

@kcocoa

kcocoa commented Jul 4, 2026

Copy link
Copy Markdown
Contributor Author

自家部署的时候发现前端 /management.html 经常打不开,往往是下载很快地到 3M~4M,然后就卡在这里再也没有流量了。
只有这个文件,只有这个请求会这样。

让 Codex 查了查,是 io.Copy 直出,到了 nginx 变成了 Transfer-Encoding: chunked 缓存出问题了。

nginx + docker 方案

@seakee

seakee commented Jul 6, 2026

Copy link
Copy Markdown
Owner

感谢 PR,看过这次改动了,方向是合理的。

新实现用 http.ServeContent 替代手动 io.Copy,能让 PANEL_PATH 下的 management.html 具备更标准的静态文件响应语义,包括 Content-LengthAccept-RangesLast-Modified,这正好对应 nginx 代理大单文件时不稳定的问题。embedded fallback 也显式补了 Content-Length,不会改变 UI 或管理 API 行为。

性能上只是在 PANEL_PATH 场景每次请求多一次 Stat 和少量条件请求判断,相对 4MB+ 的单文件 HTML 传输成本可以忽略;代理和浏览器反而能更稳定地处理缓存、范围请求和条件请求。

CI 已通过,改动范围也比较小。我这边没有看到阻塞合并的问题。后续可以考虑补充 HEAD / Range / If-Modified-Since 的测试,但不作为当前合并阻断。

@seakee
seakee merged commit 991ff87 into seakee:main Jul 6, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants