The journal · 19 July 2026 · 9 min read
HTML to PDF API security: the SSRF problem, and what to ask before you buy
A PDF renderer is a browser running inside your network, loading HTML that a user influenced. That is the exact shape of a server-side request forgery bug, and PDF generators have been a reliable place to find one for years. Here are the two attacks, the controls that stop them, and the eight questions that decide a vendor security review.
Most HTML to PDF projects hit the same wall at the same moment. The feature works, the output looks right, and then it goes to a security review and stops. The reviewer has one question you probably have not thought about: what exactly is this thing fetching, and on whose behalf?
That question is fair, and the answer matters more here than for almost any other component you will add this quarter. An HTML to PDF renderer is a browser that runs on your server and loads content that, somewhere upstream, a user influenced. That is the classic shape of a server-side request forgery bug, and PDF generators have been one of the most reliably vulnerable places to find one for the better part of a decade.
The short version
An HTML to PDF converter is a security-sensitive component because it makes outbound requests from inside your network using HTML that users can often influence. The two real risks are server-side request forgery, where the renderer is tricked into fetching internal services such as a cloud metadata endpoint and printing the response into the PDF, and local file disclosure, where an injected iframe pointing at file:///etc/passwd puts a server file in the document. Both are mitigated the same way: block local file access, restrict outbound network egress, run the renderer in an isolated sandbox with no credentials, and escape every piece of user data before it reaches the template.
Why this component in particular
Think about what happens during a render. Your code builds an HTML document, often by dropping user-supplied values into a template: a customer name, an address, a line item description, sometimes a logo URL the customer uploaded. That HTML goes to a rendering engine, which behaves like a browser. It resolves relative URLs, follows src and href attributes, loads stylesheets and images, and in a Chromium-based renderer it also runs JavaScript.
Every one of those loads is an outbound request originating from your infrastructure, with your network position. If a user can get a single tag into that HTML, they get to choose one of those destinations. The renderer has no idea the URL came from an attacker. It is doing exactly what a browser does.
This is not theoretical. Security researchers treat PDF generators as a standing category of SSRF findings, and the pattern shows up repeatedly in real products. PortSwigger's Daily Swig covered a study finding HTML to PDF converters open to denial of service, SSRF and directory traversal. Neodyme documented escalating an HTML renderer all the way from local file access to code execution. MicroStrategy had a PDF-generator SSRF assigned CVE-2020-24815. The class is well known, and it keeps recurring because the vulnerable pattern looks like ordinary templating code.
The two attacks, concretely
Local file disclosure is the simpler one. If the renderer is allowed to read from the filesystem, injected markup like <iframe src="file:///etc/passwd" width="1000" height="1000"></iframe> renders the contents of that file into the PDF, which the attacker then downloads through your own perfectly normal download button. Any file the render process can read is reachable: environment files, private keys, application configuration.
The SSRF variant is worse, because on a cloud host the interesting targets are not files. They are internal HTTP endpoints that trust anything on the local network. A request to the instance metadata service can return IAM credentials. Internal admin panels, staging services and databases with HTTP interfaces sit behind no authentication because "they are not exposed". The renderer is inside that perimeter, so from its position they are exposed.
| Attack | What the payload looks like | What it yields | Primary mitigation |
|---|---|---|---|
| Local file read | An iframe or img with a file:// URL | Server files printed into the PDF | Disable local file access in the engine |
| Cloud metadata SSRF | A request to the link-local metadata address | Cloud credentials, then everything they unlock | Egress allowlist, IMDSv2, no instance role on the renderer |
| Internal service SSRF | A URL pointing at an internal hostname or private range | Data from unauthenticated internal services | Run the renderer in an isolated network segment |
| JavaScript exfiltration | An injected script that reads then beacons out | Anything in the render context | Escape user input, restrict outbound egress |
| Render denial of service | A page designed to hang or consume memory | Renderer pool exhausted, feature down | Hard timeouts, memory caps, per-tenant concurrency limits |
If you self-host the renderer
Running your own headless Chromium, wkhtmltopdf or WeasyPrint means you own all of this. The controls that actually matter, roughly in order of payoff:
- Escape user data before it reaches the template. This is the root cause in most real findings. Values that end up in HTML need HTML escaping, not just database sanitization. If users legitimately supply rich text, run it through an allowlist sanitizer that strips
iframe,object,embedandscriptrather than trying to blocklist bad strings. - Turn off local file access. wkhtmltopdf has
--disable-local-file-accessfor exactly this. In Chromium, do not run with web security disabled and do not pass file paths in from user-reachable data. - Give the renderer its own network segment with an egress allowlist. If it only needs your CDN and your font host, it should only be able to reach your CDN and your font host. This one control neutralizes most SSRF payloads regardless of what gets injected.
- Give it no credentials. No instance role, no mounted secrets, no database access. It should be the least interesting host you own. Where the platform offers it, require IMDSv2 so metadata cannot be fetched with a plain GET.
- Sandbox the process. Run it as an unprivileged user in its own container, with a read-only filesystem where possible. Do not disable the Chromium sandbox because it was easier than fixing the container.
- Cap it. Hard render timeouts, memory limits and per-tenant concurrency, so one slow or hostile page cannot take the feature down for everyone. Our guide on timeouts on large pages covers the operational side of this.
None of that is exotic. It is a day of work if you do it up front, and an incident review if you do not.
If you buy a rendering API
Buying moves the renderer out of your network, which removes the internal-SSRF risk from your side of the boundary entirely. That is a genuine security benefit and it is the reason a lot of teams end up here after a review. What it does not do is remove your responsibility for the data in the document. Ask the vendor these, and expect specifics rather than reassurance:
| Question | What a good answer sounds like |
|---|---|
| Do you store the HTML I send, or the PDF you return? | A clear default, a stated retention window, and a way to turn storage off |
| How long is it retained, and can I delete it on demand? | A number, and an API or documented process for deletion |
| Is the render isolated per request? | A fresh browser context per render, not a shared long-lived one |
| Can renders reach arbitrary URLs I supply? | Yes, that is the product, but with abuse controls on their side |
| Is there a signed DPA, and where does processing happen? | A DPA on request and named processing regions |
| Who are the subprocessors? | A published list, with change notification |
| What compliance evidence exists? | An honest current state, not a badge that expired two years ago |
| Are documents encrypted at rest and in transit? | TLS in transit and encryption at rest, stated plainly |
The retention question is the one that decides most reviews, because it determines whether the vendor is holding your customers' invoices, statements or medical letters after the render finished. If the answer is vague, treat it as a yes. For regulated teams the same questions come back every year at audit time, which is why the answers belong in whatever system you already use for tracking obligations and mapping them to controls rather than in an engineer's memory.
Frequently asked questions
Is HTML to PDF conversion a security risk?
Yes, if user input can reach the HTML being rendered. The converter behaves like a browser inside your network, so injected markup can make it fetch internal services or local files and print the results into the PDF. The risk is well documented and entirely manageable: escape user input, disable local file access, restrict outbound egress, and run the renderer without credentials.
What is SSRF in a PDF generator?
Server-side request forgery in a PDF generator means an attacker supplies content that causes the rendering engine to make an HTTP request of the attacker's choosing from your server. Because the renderer sits inside your network, it can reach internal endpoints a remote attacker cannot, most damagingly a cloud instance metadata service that hands back credentials.
Is a hosted PDF API more secure than self-hosting?
For SSRF against your internal network, yes, because the browser no longer runs in your network at all. The trade is that your document content passes through a third party, so the security question shifts from network exposure to data handling: what they store, for how long, where, and under what contract. Which risk you prefer depends on how sensitive your documents are.
Should I let users supply the URL to convert?
Only with controls. If users pass arbitrary URLs to a renderer inside your network, you have built an SSRF endpoint on purpose. Either send the request through a rendering service outside your perimeter, or resolve and validate the URL first, rejecting private address ranges and link-local addresses, and re-validating after redirects.
Does a PDF API need a DPA under GDPR?
If the documents contain personal data and the vendor processes them on your behalf, yes, you need a data processing agreement in place and you should know the processing location. Ask before you integrate rather than during a customer security questionnaire, since a vendor without a DPA can block an enterprise deal months later.
Where we stand
Sitepdf runs the browser on our infrastructure, so nothing is rendering inside your network and the internal SSRF class does not apply to your side. Each render runs in an isolated browser context, and renders are transient by default: we return the PDF and keep nothing. Archiving is opt-in per request, with archive=true, and when you switch it on you are deliberately asking us to keep a timestamped copy, which is the point of the feature rather than a side effect. The website archiving page explains how that record works.
Being straight about the current state: we are pre-launch, so we are not going to claim a completed SOC 2 audit we do not have. If your review requires one today, that is a real reason to choose a more established vendor, and the comparison of HTML to PDF APIs names who those are. If you are weighing running your own fleet against buying, the Puppeteer alternative page covers the operational trade and what these APIs actually cost covers the money. You can try the converter at the top of this page on any URL first.
Written by the team building Sitepdf, an HTML to PDF API that archives every page it renders. The in-browser converter is free to try; early access locks the launch pricing.