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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions