mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-05-09 05:35:30 +02:00
[GH-ISSUE #1195] [Bug]: Frontend: support being served at a non-root subpath (reverse proxy / HA Ingress) #867
Labels
No labels
A1
automated
automated
bug
bug
Closed due to inactivity
contrib
dependencies
dependencies
duplicate
enhancement
feedback
hold
invalid
Notes
P1S
pull-request
security
security
ThumbsUp
user-report
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
starred/bambuddy#867
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @Spegeli on GitHub (May 2, 2026).
Original GitHub issue: https://github.com/maziggy/bambuddy/issues/1195
Originally assigned to: @maziggy on GitHub.
Component
Bambuddy
Bug Description
Reference: #1167
Bambuddy's frontend currently builds with absolute asset paths (
/assets/...,/sw-register.js), which assumes the app is always served at the host root (/). When the app is placed behind a path-based reverse proxy — including Home Assistant Ingress, Traefik with a path prefix, nginx at a subpath, Synology/Unraid reverse proxy panels, or Cloudflare Tunnels with path routing — all static assets (CSS, JS, service worker) return 404 or are served with incorrect MIME types, resulting in a blank white page.Expected Behavior
Bambuddy should load correctly when served at any subpath, not just
/. Asset references and service worker registration should be relative to whatever base path the app is deployed under, so that path-based reverse proxy setups work without modification to the core app.Steps to Reproduce
https://github.com/Spegeli/homeassistant-app-bambuddyRefused to apply style from '.../assets/index-xxx.css' because its MIME type is 'text/plain'GET .../sw-register.js → ERR_ABORTED 404 (Not Found)The same result occurs with any path-based reverse proxy (nginx
location /bambuddy/, Traefik path prefix, etc.)Printer Model
None
Bambuddy Version
Daily Beta Build v0.2.4b2-daily.20260502
SpoolBuddy Version
No response
Printer Firmware Version
No response
Installation Method
Docker
Operating System
Linux (Ubuntu/Debian)
Relevant Logs / Support Package
No response
Screenshots
No response
Additional Context
As discussed in #1167, the proposed fix is:
This is a generic fix that benefits all self-hosters behind a path-based proxy, not just HA Ingress users.
Checklist
@Spegeli commented on GitHub (May 2, 2026):
Since my English isn't the best, I had the whole thing summarized by Claude. Hope the report fits so far.
@Spegeli commented on GitHub (May 2, 2026):
Note on X-Ingress-Path and addon-side handling
In your response to #1167 you suggested that the addon should handle
X-Ingress-Pathinjection via an env var or startup arg. I want to flag that this is unfortunately not feasible from the addon side:X-Ingress-Pathis a per-request header injected dynamically by the HA Ingress gateway — it is not a static value that can be set at container startup/api/hassio_ingress/xKhWV_BGGdix.../) rotates between HA sessions, so it cannot be hardcoded or passed as an env varbaseoption)So while the addon can pass static env vars to configure runtime behaviour, it has no way to inject a dynamic, session-scoped base path into the frontend asset resolution. The fix genuinely needs to live in the core build — relative paths are the right solution, as you noted.
Happy to test any builds against HA Ingress and report back. Thanks for being open to fixing this!
I understand that you don't want to make too many special changes specifically for HA. However, one should consider that HA is the largest and most widely used smart home solution in the world (in the open-source sector) (and will likely remain so for a very long time). I think a good integration between BamBuddy and HA is very sensible, especially with regard to user growth for BamBuddy.
@Spegeli commented on GitHub (May 2, 2026):
And one more small note on:
"In the meantime — the Webpage panel path with TRUSTED_FRAME_ORIGINS is a complete alternative for users who want Bambuddy in their HA dashboard right now. Different UX from Ingress (separate URL, no HA-side SSO), but it works today without any further changes on either side."
Embedding as a frame in the dashboard works. However, only if HA is accessed via HTTP. As soon as you access your HA instance via HTTPS, it doesn't work, because HA doesn't allow connections to HTTP content once you're accessing HA via HTTPS. For example, if you're using a domain or the Nabu Casa Cloud.
And then you can't access it anymore while on the go. That's why you can also use the whole thing via HA's INGRESS function, as it allows HTTPS to HTTP.
If Home Assistant didn't have this block between HTTPS and HTTP (which unfortunately can't be bypassed), you could embed the whole thing via IFRAME in the sidebar and thus as a full page.
@maziggy commented on GitHub (May 3, 2026):
Thanks for filing this with the level of detail you did, and for accepting the framing in #1167.
You're right on every technical point in your follow-up: X-Ingress-Path is per-request and the token rotates, so it can't be injected statically; mixed-content blocks the Webpage-panel workaround the moment HA is on HTTPS; and Vite-build asset paths are baked at build time.
Asset-path fix is in. Setting base: '' in vite.config.ts tells Vite to emit relative URLs for everything — ./assets/index-.{js,css}, ./manifest.json, ./img/, ./sw-register.js — and sw-register.js now registers the service worker with a relative path so the SW scope auto-pins to whatever subpath the document was served from. This will benefit anyone behind a path-prefixed reverse proxy (Traefik path prefix, nginx subpath, Cloudflare Tunnel path routing), not just HA setups, so the change carries its own weight independent of Ingress.
HA Ingress specifically — going to be straight with you: the asset fix alone won't get the addon all the way working. After it lands, you'll get past the blank page, but API calls (/api/v1/...) still go to the host root because API_BASE is hardcoded absolute, so requests bypass the Ingress proxy and hit HA's frontend instead of Bambuddy. Making the API base, React Router basename, PWA manifest scope, and service-worker scope all subpath-aware is real work — not just the wiring, but the consequences: PWA installs done at one subpath have their scope frozen there; push-notification subscriptions are bound to SW scope; deep-link reloads need a stable anchor that the server has to inject because resolves wrong on routes like /archives/123. None of those have great answers without server-rendering the base path or carrying
significant runtime detection logic in the SPA bootstrap.
After thinking it through, the call is: core won't take on path-aware bootstrapping for HA Ingress. The supported HA embedding path stays Webpage panel + TRUSTED_FRAME_ORIGINS. For HTTPS HA, that means Bambuddy needs to be reachable over HTTPS too — either the user puts a reverse proxy in front (Caddy, nginx-proxy-manager, Traefik, etc.), or an HA addon terminates TLS inside the addon container with its own Caddy/nginx and exposes a stable URL the user embeds. That sidesteps every one of the bootstrapping edge cases above.
If you want to repurpose your addon along those lines, happy to help think through the shape — embedding a stable-URL Bambuddy via Webpage panel inside an HA-managed addon container is a perfectly reasonable design and avoids fighting Ingress. The asset-path fix from this issue still makes that addon's life easier if you ever serve at a non-root subpath.
Wiki updated with the policy at Docker → https://wiki.bambuddy.cool/getting-started/docker/?h=webpage+panel#environment-variables
Available/Fixed in branch dev and available with the next release or daily build.
If you find Bambuddy useful, please consider giving it a ⭐ on GitHub — it helps others discover the project!
@Spegeli commented on GitHub (May 5, 2026):
@maziggy First of all, I’d like to say thank you for the effort and time you put into Bambuddy and its development.
I tried the approach you suggested — terminating TLS inside the addon container with Caddy and exposing a stable HTTPS URL for the user to embed via Webpage panel. Here's what happened and why it doesn't work in practice for typical HA users.
What I did
tls internal(self-signed certificate) listening on port 8443, reverse proxying to Bambuddy on port 8000config.yamlWhere it breaks down
The core issue is the self-signed certificate. Caddy's
tls internalgenerates a locally-trusted root CA, but modern browsers (Chrome, Brave, and others) block self-signed certificates on LAN IPs entirely — there is no "proceed anyway" button shown for IP addresses, unlike hostnames. The user simply getsERR_SSL_PROTOCOL_ERRORor "invalid response" with no way to bypass it.The alternative — using a real Let's Encrypt certificate inside Caddy — requires a publicly reachable domain pointed at the addon. Many users specifically do not want to expose Bambuddy publicly for security reasons. They are happy to expose HA externally (via Nginx Proxy Manager or Nabu Casa) but want Bambuddy to remain LAN-only.
The broader picture for typical HA users
Most home HA setups look like this:
http://192.168.1.x:8123)http://192.168.1.x:8000)This means:
Conclusion
The TLS-inside-addon path only works cleanly if the user has their own domain they are willing to point at Bambuddy — which is exactly the class of user who could already solve this themselves with an external reverse proxy. For everyone else, it is a dead end.
The only solution that would work for all users without requiring them to expose Bambuddy publicly or manage certificates is proper HA Ingress support with relative API paths. I understand that is a significant undertaking — just wanted to close the loop on why the addon-side TLS approach doesn't hold up in practice.
@maziggy commented on GitHub (May 5, 2026):
Thanks for taking the time to actually try the addon-side TLS path and write up why it doesn't hold. You're right on every point — self-signed certs on raw LAN IPs are blocked by Chrome/Edge with no override, mixed-content is non-bypassable, and tls internal only solves it for users who already have a domain they could have terminated TLS at directly.
Going to be direct: Bambuddy core is not going to take on HA Ingress support. The work is not just plumbing — it is a decision to carry subpath-aware bootstrapping (API_BASE, React Router basename, PWA manifest scope, service-worker scope, push-subscription scope) for every user forever, with edge cases that break in non-obvious ways (e.g. a PWA installed under
one Ingress token has its scope frozen there when the token rotates). That cost falls on every deployment, not just the HA subset, and I am not willing to wear it.
That said — there is a workaround that actually works for the HTTPS-HA case, and I should have pointed at it in my last reply: the Nginx Proxy Manager addon for Home Assistant.
The only requirement is that the user has a domain. Anyone running HA on HTTPS externally already has one (Nabu Casa is the rare exception, and Nabu Casa users have other options). For LAN-only HTTP HA setups, the existing Webpage-panel + TRUSTED_FRAME_ORIGINS path keeps working as before.
I will add this recipe to the wiki under the Home Assistant integration section so it is easier to find than buried in this thread.
The asset-path fix from dev still ships independently and helps anyone on a path-prefixed reverse proxy — not just HA setups.
@Spegeli commented on GitHub (May 5, 2026):
Thanks for the NPM suggestion — we followed the approach and it mostly works, but there are a few caveats worth documenting for other users.
What worked
The NPM + Cloudflare Tunnel combination does solve the Mixed-Content problem. The final setup that works:
https://bambuddy.example.com(automatic TLS, no port forwarding)https://bambuddy.example.comhttps://bambuddy.example.com, which sets a Cloudflare session cookieWhat didn't work as expected
Your suggestion of using NPM's DNS-01 Let's Encrypt to expose Bambuddy without making it publicly accessible didn't hold up in practice:
172.30.x.x) as the client — not the real user IPframe-ancestors 'none'set, which means the iframe cannot trigger the login flow — the browser blocks it silentlyImportant limitation: HA Companion App
The workaround does not work in the Home Assistant Companion App on iOS/Android. The Companion App does not share browser cookies or sessions with Cloudflare Zero Trust, so the iframe either shows a blank page or a blocked response. This means the embedded Bambuddy panel is only usable via a real browser, not via the native HA app.
Note on Cloudflare Tunnel vs. direct NPM setup
It is worth noting that the NPM Access List approach (restricting access to LAN IP range only) would likely work correctly without Cloudflare Tunnel. In a direct setup where NPM handles incoming traffic without a tunnel, NPM sees the real client IP and the LAN-only restriction would function as intended — keeping Bambuddy inaccessible from outside while still allowing the iframe to load locally.
The IP masking issue is specific to Cloudflare Tunnel, which routes all traffic through its internal Docker network (
172.30.x.x) before it reaches NPM, making it impossible to distinguish between local and external clients at the NPM level. Users who do not use Cloudflare Tunnel and instead use direct port forwarding with NPM may have better results with the LAN-only Access List approach.I’ll keep experimenting a bit more, but I already want to thank you for the help. If I come up with a good and simple solution, I’ll let you know so you can include it in your wiki.