Skip to content

Desktop app: PDF/image preview breaks when the file server sends X-Frame-Options (native-iframe embed is cross-origin-fragile) #1

Description

@shanzez

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

  1. Self-host the platform behind a reverse proxy that adds X-Frame-Options: SAMEORIGIN to all responses (typical hardened nginx config).
  2. Open a PDF in Drive from the desktop app.
  3. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions