ScreenshotNeo

BlogHTML to image & PDF

How to Upload, Manage, and Display PDF Files on a Website

Learn how to upload PDFs safely, manage versions and access, and display documents with direct links or an embedded viewer.

By the ScreenshotNeo team4 October 20268 min read

To put a PDF on a website, upload it through your CMS media library or a protected application endpoint, store it under a server-generated identifier, and publish either a direct link or an embedded viewer. First decide whether the file is public or restricted: an iframe does not make a private PDF private. For user uploads, validate the actual file, restrict size and type, authorize every operation, and keep private files outside public web access.

1. Choose how PDFs enter and leave your site

The right workflow depends on who uploads documents and what visitors need to do with them.

Approach Good fit Trade-offs
Direct file link Public documents where viewing or downloading is enough Simple and broadly compatible, with fewer in-page controls
CMS media library Editors publishing a modest number of public documents Convenient workflow; check platform-specific permissions, size limits, and URL behavior
Custom upload endpoint and managed storage User submissions, private files, or tailored lifecycle rules Fine-grained control requires validation, access checks, and operational work
Embedded viewer In-page reading or viewer controls improve the task Adds JavaScript, browser, CORS, privacy, and accessibility considerations
Third-party document service Hosting or collaboration is delegated to a provider Compare provider access settings, data handling, and dependency risk

Choose public versus restricted visibility before selecting storage and delivery. Upload authentication, permission to manage a document, and permission to retrieve it are separate checks.

2. Build a safe upload workflow

Treat every submitted file as untrusted. A browser-supplied filename and Content-Type header are not proof that the content is a PDF. OWASP recommends allowlisting needed extensions, validating content, generating application filenames, limiting size, authorizing uploaders, and storing files outside the webroot or on a separate server. It also recommends considering scanning or content disarm where appropriate. See the OWASP File Upload Cheat Sheet.

  1. Require authentication and check that this user may upload to the selected account or record.
  2. Set a maximum request and file size; reject oversized input before expensive processing.
  3. Allow only the types the feature needs. Check extension and inspect the file signature or parse it with a maintained PDF library; do not trust Content-Type alone.
  4. Generate a random storage key. Keep the original name only as escaped display metadata, never as a path.
  5. Store private files outside the webroot or in private object storage. Return an application identifier, not a guessable filesystem path.
  6. Scan or sandbox files where available. If extracting or transforming PDFs, set limits for expanded size, CPU, memory, and processing time.
  7. Protect cookie-authenticated upload endpoints against CSRF. Require authorization again for replacement, deletion, and retrieval.
  8. Record administrative changes and test that changing an identifier cannot expose another user’s restricted PDF.

A safe custom endpoint has this shape; the storage and PDF inspection functions are application-specific and must be implemented with maintained libraries and your storage provider:

POST /api/documents
Authorization: Bearer <user-token>
Content-Type: multipart/form-data

file=@policy.pdf

Server-side pseudocode for the required checks:

authenticate(request)
authorize_upload(user, destination)
reject_if_request_too_large(request)
file = read_upload(request)
reject_unless_allowed_extension(file.original_name, ["pdf"])
reject_unless_pdf_signature_and_parser_accept(file.bytes)
reject_if_file_too_large(file.bytes)
scan_or_quarantine_if_configured(file.bytes)
storage_key = random_identifier()
store_outside_webroot(storage_key, file.bytes)
save_document_record(storage_key, safe_display_name(file.original_name), owner=user.id)
return {"id": public_or_scoped_identifier, "status": "stored"}

This is a checklist, not a drop-in server implementation: the authentication framework, storage client, parser, and malware scanner vary by application.

3. Manage files and revisions

Maintain a document record with a stable identifier, human-readable title, owner or responsible team, publication status, access policy, and revision date. Decide whether replacement keeps the same public URL and whether old revisions remain available. If a cached public PDF is replaced, invalidate or version the cache so visitors do not see stale content.

  • Use role-based authorization for upload, rename, publish, replace, and delete actions.
  • Keep an audit trail for changes to sensitive or important documents.
  • Separate content restrictions from access restrictions. A read-only or locked file can still be visible to anyone who can retrieve it. Google documents this distinction in its guidance on Drive content restrictions.
  • For private delivery, serve the PDF only after an authorization check or use short-lived, scoped access links appropriate to the application. Do not treat an obscure URL as authorization.

A direct link is often the most resilient option. Use descriptive link text and make the action clear. Keep the file’s public/private policy aligned with the link destination.

<a href="/documents/annual-report.pdf">View the annual report (PDF)</a>

Native browser viewer

For a public PDF, an iframe can use the browser’s built-in PDF viewer, but support and controls vary. Keep a separate link as a fallback and test mobile behavior.

<iframe
  src="/documents/annual-report.pdf"
  title="Annual report PDF"
  width="100%"
  height="720"
></iframe>
<p><a href="/documents/annual-report.pdf">Open or download the annual report PDF</a></p>

Adobe PDF Embed API

Adobe PDF Embed API is a JavaScript viewer option with multiple embed modes, including inline and lightbox presentations. Its documentation requires fileName metadata; when loading a PDF by URL, CORS configuration may be needed. Rendering can fail if the documented timeout is exceeded. Consult the API overview and how-to documentation for current setup details.

Adobe says the Embed API’s core functionality runs in a sandboxed HTML iframe. Its security documentation describes processing in Adobe Document Cloud, selectable processing regions, and TLS 1.2 or greater for content in transit. Review Adobe’s security documentation against your data requirements before sending sensitive documents to a third party.

WordPress

WordPress does not embed every arbitrary provider URL when pasted into an editor. Core maintains a provider whitelist; other providers may need oEmbed support and registration or a custom embed handler. See the WordPress oEmbed handbook. For a PDF stored in the Media Library, a plain file link is a dependable baseline; use a viewer integration only after checking its maintenance, permissions, and mobile behavior.

5. Make the document usable and accessible

  • Give the link a meaningful label, such as “Download the return policy (PDF),” rather than “click here.”
  • Provide a clear fallback link next to any embedded viewer.
  • Use document headings, meaningful reading order, selectable text, the correct document language, and text alternatives for informative graphics.
  • Check keyboard operation, zoom, small screens, and assistive technology with your supported browsers.
  • Verify the applicable WCAG and PDF accessibility requirements before making a compliance claim.

6. Or skip the browser setup

If your workflow also needs a screenshot of the page that hosts or displays a PDF, ScreenshotNeo is a website screenshot API and MCP server. Its capture can accept cookie banners and remove more than 60 known consent platforms, newsletter popups, and chat widgets before the shot; each step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers report the page verdict and billing status. AI agents can use its MCP server with Claude, Cursor, or another MCP client.

One GET request returns an image or PDF. This cURL example saves a screenshot of a page with a PDF viewer; adapt the URL to your page. See the ScreenshotNeo API documentation for parameters.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com/documents -o shot.webp

Python:

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://example.com/documents"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)

Node.js:

const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://example.com/documents'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);

The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Every feature is on every plan. Sign up for 1,000 free screenshots a month, with no card.

7. Troubleshooting

Symptom Likely cause What to check
Upload is rejected despite a .pdf suffix Content does not parse as a PDF, or the request exceeds a configured limit Check signature/parser validation and both proxy and application size limits
PDF opens as a download instead of inline Response headers or browser behavior request attachment/download Review Content-Disposition and test supported browsers; retain an explicit download link
Embedded PDF is blank or blocked Wrong URL, access denial, browser viewer behavior, or CORS for a JavaScript viewer Open the PDF URL directly, inspect network errors and permissions, and configure CORS for the actual requesting origin
Adobe viewer does not render Missing required fileName metadata, inaccessible URL, CORS issue, or rendering timeout Check the current Adobe how-to guidance and viewer/network errors
Private PDF is accessible without logging in File is in public storage or delivery bypasses authorization Move it behind an authorization-checked route or private storage; test with a separate unauthenticated session
Visitors see an old revision Browser, CDN, or proxy cache still has the previous bytes Invalidate the cache or use versioned file URLs and update the document record
WordPress shows a pasted URL as plain text The provider is not supported by the core oEmbed whitelist Use a direct link or configure a supported provider/handler as documented by WordPress
PDF upload exhausts server resources Large files or processing expansion are not bounded Set upload and parser limits, isolate processing, and use asynchronous scanning for heavier workloads

8. Performance, reliability, and cost

PDF size, storage and bandwidth, scanning, parsing, viewer scripts, and any third-party processing all affect the operating cost. Set limits that match the use case and avoid parsing or scanning synchronously in a request when the work is long-running. A managed object store can separate file delivery from application compute, but private access still needs enforcement.

For reliability, keep a stable document identifier, define replacement and deletion behavior, monitor failed processing, and preserve a direct-link fallback when embedding. Test the complete path—from upload authorization through retrieval—on the browsers and devices you support. Compare viewer choices on access control, rendering, mobile usability, download/print/search/annotation needs, integration effort, data handling, and recurring costs; the cited documentation does not establish a universal best choice.

FAQ

Can I just put the PDF URL in an iframe?

For a public file, often yes, if supported browsers render it. Keep a direct link fallback. An iframe does not enforce privacy.

Should I keep the original filename?

Keep it as safe display metadata if useful, but generate a separate storage name and never use untrusted input as a path.

Does a read-only PDF setting make it private?

No. Access control determines who can retrieve the file; content restrictions govern changes to it.

Can WordPress embed any document URL?

No. Arbitrary providers may require oEmbed support and registration or a custom handler; otherwise use a link or a compatible integration.