server { listen %%interface%%:%%port%% default_server; client_max_body_size 0; # Home Assistant opens the ingress panel at / and forwards it # as / , but Komga only answers below its servlet context path (/komga), so # bounce the panel there. absolute_redirect off keeps the Location relative # to the HA host instead of nginx's own listen address. location = / { absolute_redirect off; return 302 %%ingress_entry%%/komga/; } location / { add_header Access-Control-Allow-Origin *; proxy_connect_timeout 30m; proxy_send_timeout 30m; proxy_read_timeout 30m; proxy_pass http://127.0.0.1:25600; # Komga pushes live events over SSE (/komga/sse/v1/events), which must # not be buffered or the UI stops refreshing until the buffer fills proxy_buffering off; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; # Spring redirects /komga to /komga/ ; the Location it produces is # absolute against the upstream address, so rewrite it back onto the # ingress path (the second rule covers an already relative Location). absolute_redirect off; proxy_redirect http://127.0.0.1:25600/ %%ingress_entry%%/; proxy_redirect / %%ingress_entry%%/; # Komga scopes its cookies to the servlet context path # (Set-Cookie: ...; Path=/komga). The browser lives under the ingress # entry, so such a cookie is never sent back : login succeeds, then # every following request arrives anonymous and Komga answers 401. proxy_cookie_path /komga %%ingress_entry%%/komga; proxy_cookie_path / %%ingress_entry%%/; # Komga renders its index page with Thymeleaf @{...} link expressions, # so every asset url and window.resourceBaseUrl carry the context path # (/komga). Ingress strips its own prefix before forwarding, so the # browser needs that prefix added back. Only text/html is rewritten # (the nginx default for sub_filter_types) : the SPA derives its api # origin and router base from resourceBaseUrl at runtime, so json # responses and book pages stream through untouched. proxy_set_header Accept-Encoding ""; sub_filter_once off; sub_filter "/komga" "%%ingress_entry%%/komga"; # The epub/divina reader fetches a Readium manifest whose links Komga # builds with ServletUriComponentsBuilder.fromCurrentContextPath(), so # they are fully absolute against the upstream address nginx talks to # (http://127.0.0.1:25600/komga). Rewriting them to a root relative # ingress path also fixes the scheme : Home Assistant may be served over # https, and an absolute http:// link would be blocked as mixed content. # Only the json/xml document types are added here, so book pages are # never scanned. sub_filter "http://127.0.0.1:25600/komga" "%%ingress_entry%%/komga"; # Komga opens the reader with window.open(url, '_blank'). In the Home # Assistant companion apps the ingress panel is a webview, which hands # such a popup to an external browser : that browser carries no ingress # session cookie, so Home Assistant answers 401 before Komga is even # reached. Turn that popup into a navigation of the panel itself, but # only for the call shape Komga uses (name _blank, no feature string) # and only for http(s) urls below window.resourceBaseUrl. That leaves # the OAuth2 login popup (window.open(url, 'oauth2Login', ''), # which needs its own window), blob urls and links out of Komga alone, # and if Komga ever stopped setting resourceBaseUrl the popup is left # untouched rather than widened to the whole Home Assistant origin, # which ingress shares. Anchored on the single page app mount point : # both Komga ui shells carry it once, and only a book served as # text/html rather than the xhtml the epub spec mandates could collide # with it -- the same exposure the /komga filter above already has, and # Komga sends script-src 'none' on that endpoint. sub_filter "
" "
"; sub_filter_types application/json application/webpub+json application/divina+json application/opds+json application/atom+xml; } }