RITWIK LODHIYA
Published on

Building a Secure PDF Résumé Viewer with Next.js & hCaptcha

Authors
  • avatar
    Name
    Ritwik Lodhiya

I wanted to show my résumé online without publicly exposing the file or letting bots scrape it. The solution? A secure, blob-based PDF viewer behind an hCaptcha gate.

Here's how I built it.

🔐 Secure API Endpoint

The API route fetches the remote PDF, validates hCaptcha, caches the file in memory, and returns the content as a blob.

const token = req.nextUrl.searchParams.get('hcaptchaToken')
const verified = await hcaptchaService.verifyToken(token)

if (!verified) return new Response('Unauthorized', { status: 401 })

const pdfBuffer = await pdfService.getCachedResume()
return new NextResponse(new Blob([pdfBuffer]), {
  headers: { 'Content-Type': 'application/pdf' },
})

Caching uses node-cache with a 1-hour TTL, since Vercel functions may cold-start.

🧱 React PDF Viewer Setup

Using react-pdf, I created a clean, responsive viewer that accepts a Blob:

<Page pageNumber={pageNumber} file={pdfBlob} width={pageWidth} />

ResizeObserver is used to make the page scale automatically to 90% of the container width while preserving height.

🧪 ProtectedPdfViewer Component

This component runs hCaptcha in the client, verifies the token, fetches the PDF from the API, and passes the blob to the viewer.

{pdfBlob && <PdfViewer pdf={pdfBlob} />}

It only shows the viewer once the captcha passes.

✅ Final Features

  • 📄 Private URL for the PDF file
  • 🔒 hCaptcha-protected download
  • In-memory caching on first request
  • 📱 Responsive, scrollable PDF viewer
  • 💬 Custom navigation + UI

🚀 See It Live

You can view the final page here — assuming you’re human enough to pass the captcha 🧠