Treating nginx as both static edge and reverse proxy works best when the location map is explicit: root or alias for fingerprinted assets with long Cache-Control, and a separate proxy_pass path for the app with Host plus X-Forwarded-For and X-Forwarded-Proto so the origin sees the public scheme. I terminate TLS once at nginx, keep upstream on the private network, and enable keepalive on the upstream block with protocol 1.1 plus an empty Connection header so sockets actually reuse.
For caching I only proxy_cache idempotent GETs with a cache key that includes the host and a trimmed URI, and I leave authenticated or cookie-bearing responses uncached unless Vary is deliberate. After config changes I compare a direct upstream hit versus the edge path on status, body hash, and cache status headers — a MISS forever usually means an accidental Authorization or Set-Cookie on the response, not a broken zone. Log $upstream_addr with $upstream_cache_status for a day before trusting the dual-role setup in production.