Summary
Profile pictures never load in the browser. The backend stores and serves them correctly, but the reverse proxy in front of the stack does not forward the path they are served under, so the browser receives the single page application shell instead of the image.
Evidence
The frontend turns the stored relative path into an absolute URL against the site root, so the browser requests a path like /uploads/profile-pictures/.
Request against the backend container directly:
HTTP/1.1 200
Content-Type: image/jpeg
Content-Length: 16825
The very same path through the public host:
status 200, content type text/html, 16825 bytes replaced by the 7224 byte index.html
The file exists, it is stored in the backend container under /app/uploads/profile-pictures/. The backend registers a resource handler for /uploads/profile-pictures/** and the path is permitted in its security configuration.
Cause
docker/nginx/default.conf defines locations for /api/, /cloud-api/, /password-api/, /docs/ and /ws-chat, but none for /uploads/. The request therefore falls through to the single page application fallback, which answers every unknown path with index.html.
Suggested fix
Add a location for /uploads/ that proxies to the vault-web backend, in the same shape as the existing /api/ block. Caching headers for these files are worth considering, since the file name already contains a unique identifier per upload.
An alternative is to serve profile pictures under the existing /api prefix in the vault-web backend and to change the URL construction in the frontend accordingly. That keeps all proxy rules in one place, but it touches two repositories, so the proxy rule is the smaller step.
Note
There is a second, unrelated gap in the same area: the uploads directory needs a persistent volume, otherwise pictures are lost when the backend container is recreated.
Summary
Profile pictures never load in the browser. The backend stores and serves them correctly, but the reverse proxy in front of the stack does not forward the path they are served under, so the browser receives the single page application shell instead of the image.
Evidence
The frontend turns the stored relative path into an absolute URL against the site root, so the browser requests a path like /uploads/profile-pictures/.
Request against the backend container directly:
The very same path through the public host:
The file exists, it is stored in the backend container under /app/uploads/profile-pictures/. The backend registers a resource handler for /uploads/profile-pictures/** and the path is permitted in its security configuration.
Cause
docker/nginx/default.conf defines locations for /api/, /cloud-api/, /password-api/, /docs/ and /ws-chat, but none for /uploads/. The request therefore falls through to the single page application fallback, which answers every unknown path with index.html.
Suggested fix
Add a location for /uploads/ that proxies to the vault-web backend, in the same shape as the existing /api/ block. Caching headers for these files are worth considering, since the file name already contains a unique identifier per upload.
An alternative is to serve profile pictures under the existing /api prefix in the vault-web backend and to change the URL construction in the frontend accordingly. That keeps all proxy rules in one place, but it touches two repositories, so the proxy rule is the smaller step.
Note
There is a second, unrelated gap in the same area: the uploads directory needs a persistent volume, otherwise pictures are lost when the backend container is recreated.