server { listen %%interface%%:%%port%% default_server; client_max_body_size 0; # Kapowarr is mounted under the /kapowarr url base, so Werkzeug's # DispatcherMiddleware answers 404 at / . Home Assistant opens the ingress # panel at / unless config.yaml's ingress_entry moves it, so # bounce / to the url base whichever way the panel was opened. # 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%%/kapowarr/; } 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:5656; # Kapowarr streams queue, task and download progress over socket.io at # /api/socket.io, which must not be buffered or the UI stops # updating until the buffer fills proxy_buffering off; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; # Werkzeug redirects /kapowarr to /kapowarr/ , and the Location it # produces is absolute against the upstream address nginx talks to. # proxy_redirect puts it back on the ingress path (the second rule # covers an already relative Location). absolute_redirect off; proxy_redirect http://127.0.0.1:5656/ %%ingress_entry%%/; proxy_redirect / %%ingress_entry%%/; # Kapowarr renders every link and asset url as {{url_base}}/... and # general.js reads the same value back out of # , so rewriting the html # moves the whole SPA -- including its fetch() and socket.io urls -- # onto the ingress prefix that Home Assistant strips before forwarding. # sub_filter cannot rewrite a compressed body, hence Accept-Encoding "". proxy_set_header Accept-Encoding ""; sub_filter_once off; # text/html is always filtered ; the pwa manifest is added because # /manifest.json embeds the url base in start_url, scope, id and icons, # and it is served as application/manifest+json. Json is deliberately # not filtered : api payloads carry user file paths that must not be # rewritten. sub_filter_types application/manifest+json; sub_filter "/kapowarr" "%%ingress_entry%%/kapowarr"; } }