here’s the clean, low-risk fix that keeps your current UI and makes the in-modal PDF render work reliably:

Recommended path

Add a tiny server proxy that streams the Firebase PDF from your domain and sets Content-Type: application/pdf + Content-Disposition: inline. We also forward Range so the browser’s PDF viewer can do partial reads. Then point the modal’s <object>/<iframe> to that proxy URL.

This bypasses cross-origin embedding limits on Firebase’s signed URLs without changing your storage setup.

Micro-diffs (≤12 lines per file)
1) Server: add a proxy route

Add this block to your Express server (wherever you define routes). No new deps—uses built-in fetch and Node’s stream bridge.

// NEW: /api/preview/pdf?u=<encoded Firebase URL>&filename=optional.pdf
app.get('/api/preview/pdf', async (req, res) => {
  try {
    const u = req.query.u;
    if (typeof u !== 'string') return res.status(400).send('Missing url');
    const url = decodeURIComponent(u);
    if (!/^https:\/\/firebasestorage\.googleapis\.com\/v0\/b\//i.test(url)) return res.status(400).send('Blocked');

    const headers = {};
    if (req.headers.range) headers.Range = req.headers.range;

    const upstream = await fetch(url, { headers });
    res.status(upstream.status);
    res.setHeader('Content-Type', 'application/pdf');
    const name = (req.query.filename || 'document.pdf').toString();
    res.setHeader('Content-Disposition', `inline; filename="${name}"`);
    ['content-length','content-range','accept-ranges','cache-control','etag','last-modified']
      .forEach(h => { const v = upstream.headers.get(h); if (v) res.setHeader(h, v); });
    if (!upstream.body) return res.end();
    const { Readable } = await import('node:stream');
    Readable.fromWeb(upstream.body).pipe(res);
  } catch (e) { res.status(502).send('Upstream error'); }
});


Security note: the simple domain whitelist (firebasestorage.googleapis.com) prevents open-proxy abuse. If you ever move buckets/projects, extend that regex accordingly.

2) Frontend: point the viewer at your proxy

Where you set the src/data for your PDF preview (modal component), replace the direct Firebase URL with the proxy:

const pdfUrl = template.pdfUrl; // your existing signed URL from Storage
const proxied = `/api/preview/pdf?u=${encodeURIComponent(pdfUrl)}&filename=${encodeURIComponent(template.title || 'template.pdf')}`;


Then use proxied in your <object data=...>, <iframe src=...>, or your PDF component’s url prop.

Why this works

The browser refuses to embed Firebase’s signed URL in your page due to cross-origin/embedding headers.

By streaming it from your own domain, the embed is now same-origin and the browser’s PDF viewer happily renders it.

Forwarding Range + upstream headers keeps the viewer snappy and seekable.

Quick checklist (2 minutes)

 Route added and server restarted

 Modal switched to proxied URL

 Storage object metadata has Content-Type: application/pdf (yours already does if it opens in a new tab)

 Keep your existing “Open in new tab” fallback link as a backup

If you want to go library-based later (react-pdf/pdf.js), keep this proxy anyway—those libraries still need a CORS-clean URL.