Outbound proxy (admin)
HTTP routes and behavior for outbound proxy (admin).
GET /api/admin/outbound-proxy # { url, username, has_password } -- url carries no credentials; "" when unsetPUT /api/admin/outbound-proxy # set it ({ url, username, password }; http/https/socks5/socks5h, no path; empty url clears; blank password keeps the stored one); applied at oncePOST /api/admin/outbound-proxy/test # same body -> 204, or 400 { error: "proxy test failed: ..." } (fetches TMDB /configuration through the candidate proxy)The proxy the server’s internet-bound traffic leaves through: TMDB, Trakt (the API and the artwork relay), the hosted AI providers (Anthropic, OpenAI, Gemini, xAI Grok, and the bundled Codex app-server, which reads it from its environment: use an HTTP or HTTPS proxy there, since its SOCKS support is not something Cantinarr verifies), plex.tv, the GitHub update check, and the push relay. Every http.Client in the server declares its transport class from internal/httpx: External() is the internet-bound class and honours the stored proxy, falling back to the standard HTTP_PROXY / HTTPS_PROXY / NO_PROXY variables (Go’s semantics, lower-case names accepted) only while nothing is stored; Internal() is the cluster-internal class: arr instances, download clients, Plex Media Server, Jellyfin/Emby/Audiobookshelf, Tautulli/Tracearr, the instant-updates webhook install, the instance connection test, and the Local (OpenAI-compatible) provider: and is never proxied, not even by the env vars, so NO_PROXY never needs to name an arr host and a deployment that relied on HTTP_PROXY to reach its arrs now dials them directly. The Local provider is the one member an admin can move between classes: its endpoint is the only admin-typed one, so local_openai_use_proxy (off by default) declares it an internet host and switches that provider alone to External(), including its save-time probe and the daily health check. Cantinarr never infers the class from the URL: split-horizon DNS and Tailscale both hand out addresses a private-range test reads wrong, and reads wrong silently. A test in internal/httpx fails any http.Client literal that does not choose a class. The stored setting applies to every internet-bound host with no bypass list, so a self-hosted push relay on the LAN belongs on the env-var path with NO_PROXY. The whole setting is one encrypted outbound_proxy_url row in settings; the password is write-only (GET reports only has_password, and a PUT with a blank password keeps the stored one), url is returned without credentials, and a PUT takes effect at once with no restart. The test fetches TMDB /configuration through the candidate proxy rather than the stored one, and the 400 carries the server’s own reason, so a wrong port and a wrong password read differently. Requires instances:manage.