PDF · E-Book · Document Viewers · Delivery Design

A document viewer protects the viewer. The document is protected by how you deliver it.

Teams building a reader in the browser arrive with one question: can we stop people saving the file. The answer is that rendering requires the content, so the file is already there, and no build setting changes that. What a build setting does change is the cost of lifting the viewer you spent two years writing, which is a different asset and a better thing to be protecting.

The Short Version

Two problems, not one

Separating them makes both tractable and stops the wrong control being asked to do the work.

The document is a delivery questionWhat one authorised request returns sets the ceiling. Everything client-side sits below it.
The viewer is a code questionRendering, annotation and licensing logic are a real asset, and the transform protects exactly that.
Attribution outlives preventionA per-recipient mark applied on the server keeps working after a copy has already been made.
Separate The Assets

Four different things get called document protection

Nearly every unproductive conversation in this area comes from treating these as one problem. They have different answers and only two of them are affected by a build setting.

The viewer code

Your rendering engine, pagination, search, annotation model and any licence enforcement. This is a genuine software asset, it is often years of work, and it is the one thing on this list that a code transform directly protects. If you sell or license the viewer, this is the asset you are protecting and the case for protecting it is the ordinary one.

The document bytes

The file being displayed. This has to reach the browser in some form for rendering to happen, which means it is available to whoever is operating that browser. No build setting changes this, and any product promising otherwise is describing delivery design rather than obfuscation. What you control is the form in which it arrives and the granularity of each request.

The entitlement decision

Whether this person may see this document right now. This belongs on the server without qualification. A viewer that decides locally whether to render is enforcing a check that runs on the reader’s machine, and the general case is covered in the licence check article. Protecting the code around such a check raises the cost of finding it and changes nothing about who controls the outcome.

Attribution after the fact

Who leaked the copy that turned up somewhere it should not have. This is a different question from prevention and it has a better answer, because it keeps working after the copy exists. It is also the question rights holders usually care most about once the first conversation is over, and it is worth raising early for that reason.

The Decision That Matters

Delivery granularity decides almost everything

This is the load-bearing section. Before discussing any option, decide what one authorised request returns, because that single choice sets the ceiling on everything else you can do.

Whole-file delivery

The viewer fetches the complete original document and renders it locally. This is the simplest to build, it works offline, and it means one authorised request yields the entire document in its original form. If that is acceptable for your content, the rest of this page is largely moot and you should not spend effort on client-side measures that cannot change it.

Server-rendered pages

The server renders each page to an image or a restricted representation and the client requests them as the reader moves through the document. Assembling the whole thing now takes many authorised requests, which is loggable, rate limitable and attributable. Text selection and accessibility need deliberate design under this model, and that cost is real: an image of a page is not readable by a screen reader unless you also supply the text.

Streamed and partial rendering

A middle position where structure is fetched incrementally and content arrives per view. It keeps most of the request-granularity benefit while preserving selectable text, at the price of a more complex viewer. This is where most commercial viewers end up, and it is the model where protecting the viewer code has the clearest payoff, because that complexity is the asset.

What The Transform Does Here

Real benefits, stated narrowly

The case for protecting a viewer is good. It is just a different case from the one customers usually ask about, and conflating them is what produces disappointment later.

Reimplementation cost on the asset you own

A competitor who wants your viewer has to rebuild the layout engine, the annotation model and the interaction design rather than lifting a readable implementation. Name mangling and the string transforms remove the labels that make a bundle navigable, and the virtualised path raises it further for the specific routines you nominate. That is a commercial benefit measured in attacker hours, which is the honest unit for all of this.

Concealing the shape of your delivery API

Worth mentioning precisely because it is limited. The transform makes the endpoint and parameter construction harder to read out of the bundle, but the requests themselves are visible in any network panel as soon as the viewer runs, so the API is discoverable by using your product. Treat the delivery endpoints as public and authorise every request on their own merits, exactly as the API key article argues.

Nothing about the document itself

Stated plainly so it is not inferred otherwise: the transform operates on your JavaScript. It does not encrypt, restrict or track the document your viewer displays, and it cannot, because the document is data your code receives rather than code your build sees. Every control over the document is a delivery or rendering control.

Runtime Options

The measures customers ask for by name

Three requests come up in every conversation about this. Each has a precise answer, and in two cases the precise answer is that the measure costs more than it returns.

Right-click and key blocking

The developer-key option registers a page-wide context menu handler that cancels the event unconditionally, alongside handlers for the familiar inspection shortcuts which additionally fire the configured failure action. It stops a casual save, and it also removes a keyboard-reachable route to browser reading, translation and spell-check features for people who rely on them. The accessibility article covers that trade in full. For a document product, weigh it against the readers you are asking to work around it.

Print restriction

There is no browser mechanism that prevents printing. What exists is presentational: styles that render differently for print. A reader can defeat that with the tools already in the browser, and a server-rendered per-page model plus a visible watermark gives you both a better deterrent and an attribution trail. If printing genuinely must be restricted, that requirement belongs in your delivery model rather than in a stylesheet.

Domain locking an embedded viewer

The guard reads the hostname of the document it is executing in, and fires its failure action immediately when that value is empty, before the allowlist is consulted. Documents created from inline source, blank pages or object URLs present an empty hostname, and those are common ways to construct a viewer surface, so the guard tends to break your own frames. Embedding is controlled by a response header, as the framing article sets out.

What Keeps Working

Watermarking and server-side entitlement

Two controls survive contact with a determined reader, and both are worth more than everything in the previous section combined.

Per-recipient marks in the delivered pages

A mark identifying the account, rendered into the pages by the server rather than added by the client, travels with any copy that leaves. It converts an unattributable leak into an attributable one, which changes behaviour in a way that no client-side blocking does. Because it is applied server-side it also cannot be disabled from the browser, which is exactly the property the client-side measures lack.

Short-lived authorised URLs

Every document request carries its own authorisation, scoped to one recipient and expiring quickly. This is what stops a URL captured from a network panel being shared as a permanent public link, and it is what makes bulk retrieval visible rather than silent. It is a small amount of server work and it is the highest-value change most teams in this position can make.

A build record for the viewer itself

Separately from the documents, keep a record of what you shipped: a manifest with a source and output digest per file, a recorded seed so the build is reproducible, and the build watermark that identifies a released artefact. That is how a viewer distributed to customers stays identifiable, and the release integrity article describes the mechanics.

Frequently Asked

Questions this page gets asked

Can obfuscating our viewer stop people downloading the document?

No, and it is worth being blunt about that before any configuration is discussed. Rendering requires the content, so whatever your viewer draws from has already arrived in the browser and can be captured from there regardless of how the surrounding code was built. What protection changes is the cost of reimplementing your viewer and of lifting the logic you built into it. Whether a document can be saved is decided by your delivery design, not by your build settings.

What actually reduces bulk copying, then?

Changing what a single request yields. If a request returns the complete original file, one request is the whole document and no client-side measure alters that. If a request returns one rendered page at a time, from short-lived authorised URLs, then collecting the document means many authorised requests, which is visible in your logs, rate limitable and attributable to an account. That is a server-side design change, and it is the only one on this page that materially changes the effort involved.

Does blocking right-click and printing help?

Very little, and it has costs worth knowing before enabling it. The option registers a page-wide handler that cancels the context menu unconditionally, plus handlers for the common inspection shortcuts. Someone determined has other routes to the same content, so the measure mostly reaches ordinary users. It also removes a keyboard-reachable route to browser features people rely on, since that same event is produced by the dedicated menu key. Print restriction in a browser is a presentational hint rather than a control.

Where does watermarking fit?

It is the control that keeps working after the copy has been made, which makes it the most useful one in this category. A per-recipient mark rendered into the delivered pages on the server travels with the copy, survives being screenshotted or re-encoded to the degree you design it to, and makes a leaked document attributable. Keep it distinct from the build watermark, which tags your protected JavaScript so a released artefact can be identified. They are separate mechanisms answering separate questions.

We render the viewer in an iframe. Should we enable the domain lock?

Be careful here, because it commonly does the opposite of what is wanted. The guard reads the hostname of the document it runs in, and if that value is empty it fires the failure action before the allowlist is consulted at all. Documents created from inline source, blank pages or object URLs present exactly that empty value, and those are ordinary ways to build a viewer surface. Meanwhile a frame served from your own host matches the allowlist and authorises nothing about who embedded it. Control embedding with a response header instead.

Is any of this worth doing for a viewer we sell?

Yes, but for the other asset. If you sell the viewer itself, then the rendering engine, the annotation model, the layout and search logic and any licensing checks are the product, and raising the cost of lifting them is a reasonable commercial objective that the transform serves directly. Combine it with an entitlement decision your server makes and a per-customer build record, and you have a defensible position on the asset you actually own. The customer's documents are a different problem with a different answer.

Related Guides

Where to read next

Next Step

Decide the delivery model first, then protect the viewer

The sequence matters. Settle what one authorised request returns and whether marks are applied server-side, because those decide what is achievable. Then protect the viewer code, which is the asset a build step can genuinely defend.