Ctrl K
ComunidadGuías…

ng.conf.template

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Mirrors the "%(asctime)s [%(name)s] %(levelname)s %(message)s" format used
# by the Connect backend's Python loggers, so nginx and backend log lines
# look consistent in `docker logs`.
log_format connect '$time_iso8601 [nginx] INFO $remote_addr - "$request" $status $body_bytes_sent';

# Party guests may arrive through a TLS-terminating proxy in front of this
# one; keep the scheme it reports so connect can mark the guest cookie
# Secure. Without such a proxy, this nginx's own scheme is the answer.
map $http_x_forwarded_proto $party_forwarded_proto {
  default $http_x_forwarded_proto;
  ""      $scheme;
}

server {
  listen ${WEB_PORT};
  listen [::]:${WEB_PORT};
  sendfile on;
  default_type application/octet-stream;

  # Gated behind DEBUG=true (see start.sh) — access logs are noisy and
  # usually not needed outside of troubleshooting.
  ${NGINX_ACCESS_LOG}

  gzip on;
  gzip_http_version 1.1;
  gzip_disable      "MSIE [1-6]\.";
  gzip_min_length   256;
  gzip_vary         on;
  gzip_proxied      expired no-cache no-store private auth;
  gzip_types        text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
  gzip_comp_level   9;

  location / {
    root /usr/share/nginx/html;
    try_files $uri $uri/ /index.html =404;
  }

  # index.html isn't content-hashed, so nginx sends no Cache-Control for it
  # by default and the browser falls back to its own heuristic — meaning a
  # stale copy can outlive a redeploy for a while, referencing asset hashes
  # that no longer exist. A normal reload only re-fetches it if the browser
  # bothers to revalidate — force that every time (still cheap: a 304 when
  # unchanged).
  location = /index.html {
    add_header Cache-Control "no-cache";
    root /usr/share/nginx/html;
  }

  location = /settings.js {
    alias /etc/nginx/conf.d/settings.js;
    add_header Cache-Control "no-store";
  }

  # Everything the connect backend exposes except the three proxy-shaped
  # routes below (see connect/main.py's router list) — /config, /health,
  # /play, /discover, /volume, /join, /claim, /pairing/*, /lyrics/*, /events,
  # /status, ... all fall through to this one location. The trailing slash
  # on both the location and proxy_pass URL is what makes nginx strip the
  # "/api/" prefix down to the backend root (see connect/routes/proxy.py's
  # own "nginx strips /api/" comment, which assumes exactly this).
  location /api/ {
    proxy_pass http://127.0.0.1:${PORT}/;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Connect-Token "${CONNECT_TOKEN}";
    # /events and /visualizer are long-lived SSE streams (see
    # routes/stream.py, routes/events.py) — without proxy_http_version 1.1,
    # nginx defaults to HTTP/1.0 towards the backend, which has no chunked
    # transfer encoding, so for a response with no known Content-Length
    # (a live stream) nginx ends up batching it into internal buffers
    # instead of forwarding each chunk as it arrives. The backend already
    # sends X-Accel-Buffering: no for exactly this, but that alone wasn't
    # reliably disabling it in practice — this is the actual fix, matching
    # /rest/'s proxy_buffering off below.
    proxy_http_version 1.1;
    proxy_buffering off;
  }

  # Subsonic proxy — matches connect/routes/proxy.py's /rest/{path} route
  # directly (unprefixed), same as Electron's direct connection would hit.
  location /rest/ {
    proxy_pass http://127.0.0.1:${PORT}/rest/;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Connect-Token "${CONNECT_TOKEN}";
    proxy_buffering off;
    proxy_read_timeout 300s;
  }

  location /auth/ {
    proxy_pass http://127.0.0.1:${PORT}/auth/;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Connect-Token "${CONNECT_TOKEN}";
  }

  # Party mode's guest page and API (connect/routes/party.py). Its own
  # prefix so a reverse proxy can let exactly this past its login, and the
  # one location here that never injects X-Connect-Token: everything under
  # it authenticates with the guest's own cookie instead.
  location = /party {
    proxy_pass http://127.0.0.1:${PORT}/party;
    proxy_set_header Host $host;
  }

  location /party/ {
    proxy_pass http://127.0.0.1:${PORT}/party/;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $party_forwarded_proto;
    # The token injected for /api/ must not ride along on a request that
    # carries one of its own by accident - cleared rather than left out.
    proxy_set_header X-Connect-Token "";
    # Same SSE reasoning as /api/ above (/party/api/events).
    proxy_http_version 1.1;
    proxy_buffering off;
  }

  location /stream {
    proxy_pass http://127.0.0.1:${PORT}/stream;
    proxy_set_header X-Connect-Token "${CONNECT_TOKEN}";
    # Same reasoning as /api/'s identical two lines above: without
    # proxy_http_version 1.1, nginx speaks HTTP/1.0 to the backend, which
    # has no chunked transfer encoding, so a response with no known
    # Content-Length (this live ffmpeg-fed audio stream) gets buffered
    # internally instead of forwarded chunk-by-chunk as it arrives — the
    # backend's own X-Accel-Buffering: no header alone isn't reliably
    # enough to prevent that. Matters even more here than for /api/: a cast
    # device pulling this in real time needs the data flowing continuously,
    # not held back by nginx and then released in an unpredictable burst.
    proxy_http_version 1.1;
    proxy_buffering off;
  }
}