Summary
In the desktop app, PDF (and image) preview renders blank when the self-hosted server returns X-Frame-Options: SAMEORIGIN on file responses. X-Frame-Options: SAMEORIGIN is a near-universal reverse-proxy hardening default (nginx, most security-header snippets, Mozilla Observatory guidance), so many self-hosted deployments hit this. The same underlying design also makes PDF preview fail in Safari.
Root cause
- The desktop app serves its front bundle from a local origin (packaged app), but fetches files from the configured server origin (
FILES_URL). File embeds are therefore cross-origin.
EmbeddedPDF.svelte (packages/ui/src/components/EmbeddedPDF.svelte) renders the PDF with a native iframe: <iframe src={src + '#view=FitH&navpanes=0'}> (introduced in #10491, which removed the earlier blob-based path).
- A native iframe pointed at a cross-origin URL is refused by the browser when that response carries
X-Frame-Options: SAMEORIGIN. Blink logs: "Refused to display '…' in a frame because it set 'X-Frame-Options' to 'sameorigin'." → blank preview.
- Independently, the same native-iframe approach renders blank in Safari/WebKit even same-origin, because WebKit does not display PDFs inside an iframe (it offers to download instead).
Impact
- Every self-hosted deployment whose reverse proxy sets
X-Frame-Options on /files (a common, recommended default) → no PDF/image preview in the desktop app.
- Safari users (web) → no PDF preview, regardless of headers.
Steps to reproduce
- Self-host the platform behind a reverse proxy that adds
X-Frame-Options: SAMEORIGIN to all responses (typical hardened nginx config).
- Open a PDF in Drive from the desktop app.
- Preview is blank; devtools console shows the
X-Frame-Options refusal for the /files/... request.
Suggested fix
Render PDFs with pdf.js (fetch the bytes via the normal authenticated fetch, render to <canvas>) instead of a native <iframe>. pdf.js is immune to X-Frame-Options/frame-ancestors/CSP on the file response and to per-browser native-PDF-viewer differences, so it fixes both the cross-origin/desktop case and Safari in one change. (Confirmed pdf.js renders in WebKit where the native iframe is blank.) Alternatively, proxy files through the app's local origin so embeds are same-origin.
Workaround for self-hosters
Exclude the file route from X-Frame-Options at the proxy, e.g. an nginx location /files { … } that re-adds X-Content-Type-Options/Referrer-Policy but omits X-Frame-Options (defining any add_header in the location stops inheritance of the server-level one). The app UI keeps SAMEORIGIN.
Filed by the Exaviz team from a self-hosted deployment. Happy to open a PR for the pdf.js approach if that direction is welcome.
Summary
In the desktop app, PDF (and image) preview renders blank when the self-hosted server returns
X-Frame-Options: SAMEORIGINon file responses.X-Frame-Options: SAMEORIGINis a near-universal reverse-proxy hardening default (nginx, most security-header snippets, Mozilla Observatory guidance), so many self-hosted deployments hit this. The same underlying design also makes PDF preview fail in Safari.Root cause
FILES_URL). File embeds are therefore cross-origin.EmbeddedPDF.svelte(packages/ui/src/components/EmbeddedPDF.svelte) renders the PDF with a native iframe:<iframe src={src + '#view=FitH&navpanes=0'}>(introduced in #10491, which removed the earlier blob-based path).X-Frame-Options: SAMEORIGIN. Blink logs: "Refused to display '…' in a frame because it set 'X-Frame-Options' to 'sameorigin'." → blank preview.Impact
X-Frame-Optionson/files(a common, recommended default) → no PDF/image preview in the desktop app.Steps to reproduce
X-Frame-Options: SAMEORIGINto all responses (typical hardened nginx config).X-Frame-Optionsrefusal for the/files/...request.Suggested fix
Render PDFs with pdf.js (fetch the bytes via the normal authenticated
fetch, render to<canvas>) instead of a native<iframe>. pdf.js is immune toX-Frame-Options/frame-ancestors/CSP on the file response and to per-browser native-PDF-viewer differences, so it fixes both the cross-origin/desktop case and Safari in one change. (Confirmed pdf.js renders in WebKit where the native iframe is blank.) Alternatively, proxy files through the app's local origin so embeds are same-origin.Workaround for self-hosters
Exclude the file route from
X-Frame-Optionsat the proxy, e.g. an nginxlocation /files { … }that re-addsX-Content-Type-Options/Referrer-Policybut omitsX-Frame-Options(defining anyadd_headerin the location stops inheritance of the server-level one). The app UI keepsSAMEORIGIN.Filed by the Exaviz team from a self-hosted deployment. Happy to open a PR for the pdf.js approach if that direction is welcome.