Skip to content

fix(qbittorrent): add URL Base support for reverse-proxied instances#772

Open
schmitzkr wants to merge 1 commit into
Listenarrs:canaryfrom
schmitzkr:fix/qbittorrent-url-base
Open

fix(qbittorrent): add URL Base support for reverse-proxied instances#772
schmitzkr wants to merge 1 commit into
Listenarrs:canaryfrom
schmitzkr:fix/qbittorrent-url-base

Conversation

@schmitzkr

Copy link
Copy Markdown

Summary

Adds a "URL Base" field to the qBittorrent download client (closes #690), so Listenarr can reach qBittorrent instances served behind a path-prefixed reverse proxy (e.g. example.com/qbittorrent) — matching the field Transmission's client already has.

I deliberately didn't just copy Transmission's mechanism. Transmission's urlBase replaces its whole RPC path, because that mirrors Transmission's own daemon setting (--rpc-url-base) — Listenarr just matches whatever arbitrary path the user configured server-side. qBittorrent has no equivalent: I checked upstream, and a native WebUI base-path setting has been proposed twice (qbittorrent/qBittorrent#21471, closed unmerged; #23467, still open/unmerged) — neither has shipped. So every real qBittorrent-behind-a-proxy deployment today works by the reverse proxy stripping a path prefix before it reaches qBittorrent, which always serves at its own root regardless. That means the correct semantics here are a prefix prepended before the fixed /api/v2/... routes, not a full-path override.

Changes

Added

  • QBittorrentHelpers.BuildBaseUrl() — composes authority + normalized urlBase prefix from client settings; no-op when unset, so existing configs are unaffected
  • "URL Base" field now renders for qBittorrent in the download client form, with qBittorrent-specific placeholder/help text (distinct from Transmission's field, since the semantics differ)
  • Backend test asserting the actual HTTP request hits /qbittorrent/api/v2/app/version when configured
  • Frontend test asserting the field renders for qBittorrent and flows into the save/test payload

Changed

  • The 8 qBittorrent workflow files that each independently called DownloadClientUriBuilder.BuildAuthority(client) now go through the new helper instead (mechanical, one line each — no other behavior change)

Testing

  • dotnet build / dotnet format --verify-no-changes clean
  • Targeted backend tests (qBittorrent + Transmission): 71/71 pass
  • Full backend suite: 1185/1190 pass (5 pre-existing failures unrelated to this change — diacritics-normalization tests that only fail in an ICU-less sandbox, not a normal dev setup)
  • Frontend: Prettier / vue-tsc clean, full suite 393/393 pass
  • Manually tested against a real reverse-proxied qBittorrent instance and confirmed the connection works with the new URL Base field set

Notes

AI assistance disclosure: this PR was developed with Claude Code's assistance - the implementation (the helper, the 8-file sweep, the frontend field, and the tests) was written by Claude Code. My role was directing the approach and validating the result: I pushed back on an initial draft that assumed we should just copy Transmission's urlBase mechanism, which prompted the investigation that found qBittorrent needs different (prefix, not replace) semantics - that upstream research and the resulting design decision are reflected above. I also manually tested this against my own qBittorrent instance behind a real reverse proxy and confirmed it resolves the actual problem reported in #690, which is real, live validation beyond the automated test suite. I have not done a line-by-line manual read of the full diff myself - flagging that plainly since this is my first PR on a project I don't maintain, and I'd rather be upfront about the split between direction/testing and code-writing than imply a level of manual review that didn't happen.

One unrelated thing noticed while auditing every place qBittorrent builds a URL: there are 5 separate, independently-written login implementations across these files (pre-existing, not introduced by this change), and only one of them sets a Referrer header that qBittorrent's CSRF check can care about. It shouldn't interact with urlBase (qBittorrent's CSRF/Referer check is origin-based, not path-based), so I left it alone here, but it seemed worth mentioning in case it's the cause of a future report like "Test Connection works but adding a torrent fails."

qBittorrent's download client only had Host/Port, so it always connected at the
root path and couldn't reach instances served behind a path-prefixed reverse
proxy (Transmission already had this field). Unlike Transmission's urlBase,
which replaces the whole RPC path to match Transmission's own configurable
--rpc-url-base daemon setting, qBittorrent has no equivalent server-side base
path setting (upstream qbittorrent/qBittorrent#21471 and #23467 are both
unmerged), so this prepends urlBase as a plain prefix before the fixed
/api/v2/... routes instead of replacing them.

Closes Listenarrs#690.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
@schmitzkr
schmitzkr marked this pull request as ready for review July 20, 2026 20:23
@schmitzkr
schmitzkr requested a review from a team July 20, 2026 20:23
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.

qBittorrent download client missing URL Base field

1 participant