Serverless6 min read · June 13, 2026

Serverless PDF Generation: Vercel, AWS Lambda & Cloudflare Workers

Puppeteer doesn't work on serverless. Here's how to generate PDFs on Vercel, AWS Lambda, and Cloudflare Workers using a REST API. Just a fetch call.

Puppeteer works fine locally, but breaks when you deploy to a serverless environment. Chromium's unzipped size exceeds AWS Lambda's 250 MB code limit. Vercel Edge Functions block Node.js native modules. Cloudflare Workers have no file system.

A hosted REST API sidesteps every one of these constraints. From serverless, PDF generation is just an HTTP POST.

Vercel: App Router Route Handler

app/api/pdf/route.tsts
export async function POST(request: Request) {
  const { html } = await request.json()

  const res = await fetch('https://platform.htmltopdfapi.co/api/v1/pdf/generate', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.HTMLTOPDF_API_KEY!}`,
      'Content-Type': 'application/json',
      Accept: 'application/pdf',
    },
    body: JSON.stringify({ html, paper_size: 'a4' }),
  })

  if (!res.ok) return Response.json({ error: 'PDF failed' }, { status: 500 })

  return new Response(await res.arrayBuffer(), {
    headers: { 'Content-Type': 'application/pdf' },
  })
}

AWS Lambda (Node.js)

handler.tsts
import type { APIGatewayProxyHandlerV2 } from 'aws-lambda'

export const handler: APIGatewayProxyHandlerV2 = async (event) => {
  const { html } = JSON.parse(event.body ?? '{}') as { html: string }

  const res = await fetch('https://platform.htmltopdfapi.co/api/v1/pdf/generate', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.HTMLTOPDF_API_KEY}`,
      'Content-Type': 'application/json',
      Accept: 'application/pdf',
    },
    body: JSON.stringify({ html, paper_size: 'a4' }),
  })

  if (!res.ok) return { statusCode: 500, body: JSON.stringify({ error: 'PDF failed' }) }

  return {
    statusCode: 200,
    headers: { 'Content-Type': 'application/pdf' },
    body: Buffer.from(await res.arrayBuffer()).toString('base64'),
    isBase64Encoded: true,
  }
}

Cloudflare Workers

worker.tsts
export default {
  async fetch(request: Request, env: { HTMLTOPDF_API_KEY: string }): Promise<Response> {
    const { html } = await request.json() as { html: string }

    const res = await fetch('https://platform.htmltopdfapi.co/api/v1/pdf/generate', {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${env.HTMLTOPDF_API_KEY}`,
        'Content-Type': 'application/json',
        Accept: 'application/pdf',
      },
      body: JSON.stringify({ html, paper_size: 'a4' }),
    })

    if (!res.ok) return Response.json({ error: 'PDF failed' }, { status: 500 })
    return new Response(res.body, { headers: { 'Content-Type': 'application/pdf' } })
  },
}

Why This Works Everywhere

The pattern is identical across every serverless platform: send an HTTP POST, receive PDF bytes. No binary to bundle, no native module to compile, no Lambda layer to maintain. The rendering happens on the API infrastructure. Your function is just a thin proxy.