Skip to content

docs(reverse-proxy): tune the Nginx Proxy Manager guidance - #132

Open
Spinnich wants to merge 2 commits into
rommapp:mainfrom
Spinnich:docs/npm-reverse-proxy-tuning
Open

docs(reverse-proxy): tune the Nginx Proxy Manager guidance#132
Spinnich wants to merge 2 commits into
rommapp:mainfrom
Spinnich:docs/npm-reverse-proxy-tuning

Conversation

@Spinnich

@Spinnich Spinnich commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

What

Rewrites the Nginx Proxy Manager section of install/reverse-proxy.md.

The page previously recommended a single Advanced line:

proxy_max_temp_file_size 0;

with the explanation that "large downloads will fail on NPM because nginx tries to buffer them to disk". That is not what happens, and the line on its own makes downloads slower.

How this was checked

I rebuilt NPM's nginx configuration locally from its own sources (docker/rootfs/etc/nginx/nginx.conf plus the proxy_host.conf / _assets.conf / _forced_ssl.conf templates on develop) and ran the real generated config in a container against a RomM-shaped origin, so every claim below is measured rather than inferred. Numbers are a 512 MB object over a 50 ms RTT link, and a 1 GiB upload.

Advanced config Download speed
proxy_buffering off 71 to 75 MB/s
Current docs (proxy_max_temp_file_size 0 alone) 116 to 121 MB/s
NPM stock defaults 133 MB/s
This PR 136 to 139 MB/s

Findings that drove the rewrite:

  • Downloads do not fail without the line. A 512 MB download through stock NPM returns 200 with all 536,870,912 bytes. What actually happens is an upstream response is buffered to a temporary file in the error log, spooling up to proxy_max_temp_file_size (nginx default 1 GB) per in-flight download into the NPM container.
  • The old one-liner costs throughput. It removes the spill but leaves NPM's default 8x4k buffers, so there is almost no read-ahead left. Pairing it with real buffers is what makes it a win.
  • client_max_body_size is required. NPM's global default is 2000m, so a 3 GB ROM upload is rejected with 413 before any bytes are read.
  • proxy_request_buffering off genuinely streams. With a 1 GiB chunked upload, stock NPM hands the origin a Content-Length: 1073741824 request, meaning it buffered the whole body to /tmp/nginx/body inside its own container first. With the flag off the origin sees Transfer-Encoding: chunked.
  • Websockets Support off breaks Socket.IO. NPM strips Upgrade/Connection, the origin sees no upgrade request and rejects the handshake, so scan progress falls back to long polling. The page already said on, this adds the reason.
  • Cache Assets on damages RomM's caching. NPM's assets.conf turns Cache-Control: public, max-age=31536000, immutable into max-age=21258 plus an Expires, and drops Last-Modified and Vary: Accept-Encoding while still serving a gzip-encoded body from a shared cache. It also pins proxy_read_timeout 45s on those requests. The page already said off, this adds the reason.
  • Trust Upstream Forwarded Proto Headers is a Force SSL bypass at the edge. With it on, a plain HTTP request carrying X-Forwarded-Proto: https gets 200 instead of the 301, and RomM receives X-Forwarded-Proto: https for a cleartext connection. It is only appropriate when NPM sits behind another TLS terminator.
  • Compression. RomM's own nginx gzip_types omits image/svg+xml and application/wasm, so those cross the wire uncompressed (Accept-Encoding does reach RomM, so JS/CSS/JSON are already handled and are not double compressed). RomM ships 372 SVGs totalling 4.2 MB that compress to 1.05 MB, and 25.6 MB of wasm that compresses about 63%. .data core files are already compressed and are correctly left out.

Verified alongside: Range/resume is byte exact through the new block (206, matching MD5 at a 400 MB offset), websocket frames still arrive in real time under proxy_buffering on, and nginx -t accepts the buffer arithmetic.

Notes for review

  • I changed client_max_body_size to 0 rather than a large finite value, to match the Nginx section higher on the same page. Happy to switch it back to a cap if you would rather have one.
  • Screenshots have been re-shot to match the recommended settings (the old Details one had Cache Assets on, contradicting the text). The Advanced capture is dropped: that tab is now a copyable code block, so a screenshot of it would only drift. The table moved up next to the Details and SSL sections it illustrates.
  • mkdocs build --strict passes and trunk fmt / trunk check are clean.

AI assistance

Written with substantial AI assistance (Claude Code). The prose, the config block, and the test harness were AI-authored; the measurements above were produced by running that harness against real NPM configuration and the RomM 5.1.0-alpha.1 image, and I reviewed the result.

🤖 Generated with Claude Code

NPM's defaults get in the way of multi-GB ROM transfers, and the single
`proxy_max_temp_file_size 0` line the page recommended removed the disk
spill without replacing the read-ahead it relied on.

Replace it with a full Advanced block covering uploads, download
buffering, timeouts, and compression, and document the three UI toggles
that measurably change behaviour: Websockets Support, Cache Assets, and
Trust Upstream Forwarded Proto Headers.

Also correct the old explanation. Large downloads do not fail without
that line; they succeed and spool up to 1 GB per connection to the NPM
container's filesystem.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Spinnich

Spinnich commented Sep 7, 2026

Copy link
Copy Markdown
Contributor Author
image image

Replace the Details and SSL captures with ones that match the settings
the page recommends. The old Details shot had Cache Assets on, which
contradicted the text.

Drop the Advanced capture: that tab is now a code block readers can copy,
so a screenshot of it only risks drifting out of sync. Move the table up
next to the Details and SSL sections it illustrates.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Spinnich
Spinnich requested a review from gantoine September 7, 2026 20:29
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.

1 participant