PDFPipe

The request or the response

413 Payload Too Large when sending HTML to render

Small documents render and large ones are rejected before anything happens. The threshold is consistent, which distinguishes this from a timeout.

What is actually happening

The request body exceeded a limit. Almost always this is base64 images inside the HTML: base64 adds about a third to the size of every embedded asset, so a handful of photos turns a 40KB document into a request of several megabytes.

Confirming it is this and not something that looks like it

Log the byte length of the request body. If it is far larger than the visible HTML, embedded assets are the cause.

The fix

Keep large images as URLs and reserve embedding for small fixed assets like logos. Resize before embedding if you must embed. The rule that works is a size threshold rather than a judgement per image.

python
EMBED_LIMIT = 100 * 1024  # bytes, before base64 expands it by a third

def asset_src(path: Path, public_url: str) -> str:
    """Small and fixed: embed it, and remove a network dependency.
    Large or user-supplied: link it, and keep the request small."""
    if path.stat().st_size <= EMBED_LIMIT:
        return data_uri(path)
    return public_url

If that was not it

These produce the same symptom often enough to be worth ruling out before assuming the fix above did not work.

  • An embedded font, which is often larger than any single image
  • A proxy with a lower body limit than the endpoint itself
  • Repeating the same base64 image on every row of a table
  • Sending a whole CSS framework inline when the document uses a dozen rules

Frequently asked

Does this happen with every rendering engine?

The behaviour behind it is not specific to one tool. The request body exceeded a limit. Anything rendering HTML to a paged medium has to make the same decision, so the fix travels with you if you change how the render happens.

Will this show up as an error in my logs?

Yes, this one surfaces as a thrown error or a failed status, which is why it is at least findable. The harder half is that the message often names the call that was in flight rather than the thing that actually failed.

Related failures

Problems people arrive at from the same starting point, or mistake for this one.

Paste your markup and see the rendered document, without signing up.