If you’ve ever embedded a PDF, DOCX, or PPTX with Google Docs Viewer, you’ve probably hit the same wall everyone does: your Content Security Policy breaks it.

That’s not a bug. That’s CSP doing its job.

The annoying part is that Google Docs Viewer sits in an awkward middle ground. It’s convenient, familiar, and dead simple to drop into an <iframe>. But once you care about a tight CSP, third-party framing gets messy fast. You have to decide how much trust you want to hand to Google for a document-rendering feature that may not even be essential.

Here’s the practical comparison guide I wish more people wrote.

The basic problem

Google Docs Viewer is usually embedded like this:

<iframe
  src="https://docs.google.com/gview?embedded=1&url=https://example.com/files/report.pdf"
  width="100%"
  height="700"
  loading="lazy">
</iframe>

If your CSP has a restrictive frame-src, that embed won’t load unless https://docs.google.com is allowed.

At minimum, you’re looking at something like:

Content-Security-Policy: default-src 'self'; frame-src 'self' https://docs.google.com;

That’s the simplest answer. It’s also not always the best one.

If you want a refresher on directives like frame-src, default-src, and frame-ancestors, csp-guide.com is a good reference without a lot of fluff.

Option 1: Allow Google Docs Viewer directly

This is the common setup.

Example CSP

Content-Security-Policy:
  default-src 'self';
  frame-src 'self' https://docs.google.com;
  object-src 'none';
  base-uri 'self';
  form-action 'self';
  frame-ancestors 'none';

Pros

  • Fastest path to shipping
  • Works well for public documents
  • No need to build your own document rendering stack
  • Keeps your app code simple

Cons

  • You’re trusting a third-party origin in frame-src
  • Viewer availability depends on Google
  • Some files render inconsistently
  • Private or signed URLs can be painful
  • You get less control over UX and privacy

My opinion: this is acceptable for low-risk public content, especially brochures, whitepapers, manuals, and marketing docs. I would not use it casually for anything sensitive, internal, or access-controlled.

Option 2: Allow broad Google origins “just in case”

I still see teams do this:

Content-Security-Policy:
  default-src 'self';
  frame-src 'self' https://docs.google.com https://*.google.com https://*.gstatic.com;

Usually this happens after a few rounds of “it still doesn’t work in staging” and someone starts adding domains until the embed stops complaining.

Pros

  • Lower chance of breakage from undocumented Google dependencies
  • Faster debugging when you’re under deadline pressure

Cons

  • Sloppy trust boundary
  • Harder to audit later
  • Easier to normalize overbroad CSP exceptions
  • Usually unnecessary

I don’t recommend this unless you’ve actually observed a required dependency and documented why it exists. CSP exceptions have a way of becoming permanent. Temporary allowances almost never get cleaned up.

Option 3: Use a dedicated document route and isolate the policy

This is my preferred compromise when a product really wants Google Docs Viewer.

Instead of allowing docs.google.com across your whole application, serve the viewer from a dedicated route with a narrower purpose and a route-specific CSP.

For example:

  • /docs/viewer/:id → page that only hosts the iframe
  • Main app pages keep a stricter CSP
  • Viewer page gets a targeted frame-src https://docs.google.com

Example route-specific CSP

For the rest of the app:

Content-Security-Policy:
  default-src 'self';
  frame-src 'self';
  object-src 'none';
  base-uri 'self';
  form-action 'self';
  frame-ancestors 'none';

For /docs/viewer/:id:

Content-Security-Policy:
  default-src 'self';
  frame-src https://docs.google.com;
  script-src 'self';
  style-src 'self';
  img-src 'self' data:;
  object-src 'none';
  base-uri 'self';
  form-action 'self';
  frame-ancestors 'none';

Pros

  • Limits third-party framing to one page
  • Easier to reason about risk
  • Cleaner security review
  • Better than weakening the whole app

Cons

  • More app and infrastructure work
  • Requires per-route header support
  • Still depends on Google

This is the version I’d defend in a security review. If you need the feature, isolate it.

Option 4: Don’t use Google Docs Viewer at all

Sometimes the best CSP for Google Docs Viewer is no Google Docs Viewer.

Alternatives include:

  • Native browser PDF rendering
  • Self-hosted PDF.js
  • Server-side document conversion
  • Download-only flow

Pros

  • Stronger privacy story
  • Tighter CSP
  • More predictable availability
  • Better control over UI and access checks

Cons

  • More engineering effort
  • Non-PDF formats are harder
  • Conversion pipelines can get ugly

For PDFs, I’d seriously question why Google Docs Viewer is needed in 2026. Modern browsers already do a decent job. For Office docs, the calculus is different, but I’d still ask whether preview is truly required.

What not to change

A common mistake is loosening unrelated directives because the viewer doesn’t load.

Google Docs Viewer in an iframe usually affects frame-src. It does not mean you need to weaken:

  • script-src
  • style-src
  • object-src
  • base-uri
  • form-action

If your app currently has a decent baseline, keep it.

For example, this real-world CSP from HeaderTest is a good example of staying explicit and tight in the places that matter:

content-security-policy: default-src 'self' https://www.googletagmanager.com https://*.cookiebot.com https://*.google-analytics.com; script-src 'self' 'nonce-MmQ2ODQ5Y2YtMzNhZC00MGQ3LWFlNDItNTQ4OTdlNzU0NGM0' 'strict-dynamic' https://www.googletagmanager.com https://*.cookiebot.com https://*.google-analytics.com; style-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://*.cookiebot.com https://consent.cookiebot.com; img-src 'self' data: https:; font-src 'self'; connect-src 'self' https://api.headertest.com https://tallycdn.com https://or.headertest.com wss://or.headertest.com https://*.google-analytics.com https://*.googletagmanager.com https://*.cookiebot.com; frame-src 'self' https://consentcdn.cookiebot.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; object-src 'none'

Notice what’s happening there:

  • frame-src is specific
  • object-src 'none' stays locked down
  • base-uri 'self' and form-action 'self' are explicit
  • The policy doesn’t randomly broaden everything for one embed

That’s the mindset you want.

Minimal change for Google Docs Viewer

Content-Security-Policy:
  default-src 'self';
  frame-src 'self' https://docs.google.com;
  object-src 'none';
  base-uri 'self';
  form-action 'self';
  frame-ancestors 'none';

Use this if:

  • documents are public
  • the route is low risk
  • you need the feature quickly

Safer isolated route approach

Content-Security-Policy:
  default-src 'self';
  frame-src https://docs.google.com;
  script-src 'self';
  style-src 'self';
  img-src 'self' data:;
  object-src 'none';
  base-uri 'self';
  form-action 'self';
  frame-ancestors 'none';

Use this if:

  • you can set headers per route
  • you want tighter blast-radius control
  • security review matters

Strict no-third-party viewer approach

Content-Security-Policy:
  default-src 'self';
  frame-src 'self';
  object-src 'none';
  base-uri 'self';
  form-action 'self';
  frame-ancestors 'none';

Use this if:

  • you render documents yourself
  • you only support direct download
  • you want the simplest trust model

Extra gotchas

frame-src vs child-src

If you’re supporting older CSP setups, you may still see child-src. Modern policies should prefer frame-src for frames. Don’t cargo-cult both unless you have a compatibility reason.

frame-ancestors is unrelated

frame-ancestors controls who can embed your page. It does not grant permission for you to embed Google Docs Viewer. I still see this confused in production configs.

Signed URLs may fail

If your documents are behind expiring URLs, Google’s fetch behavior can cause flaky previews. A URL that works fine in a browser tab may fail when passed to gview.

Privacy and compliance questions

When you send a document URL to a Google-hosted viewer, that choice has policy implications. Your legal or compliance team may care more than engineering does. Fair warning.

My take

If I had to rank the options:

  1. Self-host or native rendering for anything serious
  2. Isolated viewer route if Google Docs Viewer is a product requirement
  3. Direct frame-src https://docs.google.com for low-risk public content
  4. Broad Google allowlists only when you have hard evidence and no better option

The real trap isn’t using Google Docs Viewer. The real trap is using it and then quietly weakening your CSP across the entire app because “it was easier.”

That trade is almost never worth it.