Back to blog
API SecurityScreenshot APIDeveloper Tools

Stop API Key Theft: Locking Your Screenshot API to Trusted Origins

Learn how screenshot API origin locking secures your keys from abuse, with practical setup for referer checks, allowlists, and signed URLs.

2026-05-316 min read

If your screenshot API key is exposed in a frontend bundle, on a public dashboard, or in a misconfigured serverless function, somebody will eventually find it. And when they do, they'll burn through your quota generating PDFs of questionable websites until your bill spikes or your service shuts down. Origin locking is the single most effective defense against this — and most developers either skip it entirely or configure it badly.

This post walks through how origin locking actually works for screenshot APIs, where it falls short, and what to combine it with so your keys stay yours.

Why screenshot API keys get abused

Screenshot APIs are an attractive target for abuse because each request does real work: spinning up a headless browser, rendering JavaScript, capturing pixels, encoding output. That makes every leaked key a free rendering farm for whoever finds it. Common leak vectors:

  • Frontend code — calling the API directly from a React or Vue app exposes the key in network requests.
  • Public repos — committed .env files and hardcoded test scripts.
  • Client-side OG image generators — embedding the key in <img src="..."> tags.
  • Logs and error trackers — Sentry or Datadog payloads that capture full request URLs.

Once a key escapes, attackers use it within hours. Bot networks crawl GitHub commits, npm packages, and Pastebin specifically for this.

What origin locking actually does

Origin locking ties an API key to a specific set of domains. When a request comes in, the screenshot API checks the Origin or Referer header against your allowlist. Mismatches get rejected before any rendering happens.

This works because browsers automatically attach these headers and won't let JavaScript forge them. An attacker copying your key into their own site will see requests blocked at the edge — the headers will betray them.

The limits you need to understand

Origin locking is not bulletproof. Three things to keep in mind:

  1. Server-to-server calls have no origin. A curl request or backend script can set any Referer header it wants. Origin locking only protects browser-originated traffic.
  2. Subdomain wildcards are risky. Allowing *.yourdomain.com means any subdomain takeover or open redirect on your domain becomes an exploit path.
  3. It's a layer, not a fortress. Combine it with rate limits, usage caps, and separate keys per environment.

Setting up origin locking in practice

Most modern screenshot APIs, including PxShot, support origin restrictions through the dashboard. The general flow:

  1. Create a dedicated key for browser use (separate from your server key).
  2. Add allowed origins as exact matches: https://app.example.com, https://example.com.
  3. Avoid trailing slashes and mixed protocols — be explicit.
  4. Set a daily request cap on the key so even a successful breach is bounded.

Frontend example

Here's a browser-safe OG image preview using an origin-locked key:

<img
  src="https://api.pxshot.dev/v1/screenshot?url=https://example.com&key=pk_browser_xxx&width=1200&height=630"
  alt="Preview"
/>

If someone copies that URL and embeds it on their own site, the Referer header will point to their domain and the request will be rejected. The key is visible but useless off-origin.

Backend example with signed requests

For server-to-server calls where origin locking doesn't apply, use a separate secret key and never expose it client-side:

const res = await fetch('https://api.pxshot.dev/v1/screenshot', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.PXSHOT_SECRET_KEY}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    url: targetUrl,
    format: 'pdf',
    full_page: true
  })
});

Defense in depth: what to layer on top

Origin locking handles browser abuse. Here's what catches the rest:

1. Per-key usage caps

Set a hard daily or monthly ceiling on each key. If your normal traffic is 5,000 screenshots a day, cap the key at 7,000. A leaked key can't drain your account overnight.

2. URL allowlists

If you only screenshot your own properties, restrict the target URL pattern. This kills the attacker's main use case — they want to screenshot arbitrary sites, not just yours.

3. Signed URLs for sensitive flows

For PDF generation of invoices or private dashboards, use short-lived signed URLs generated server-side. The client never touches the screenshot API directly.

4. Separate keys per environment

Development, staging, and production should each have their own keys with their own origins:

  • pk_dev_xxx — locked to localhost:3000 and *.ngrok.io
  • pk_staging_xxx — locked to staging.app.com
  • pk_prod_xxx — locked to app.com

5. Monitor and alert on anomalies

Watch for unusual patterns: sudden traffic spikes, requests from new IP ranges, repeated 403s from unexpected origins. PxShot's dashboard surfaces blocked requests so you can see attempted abuse in real time and react before it becomes a problem.

Common configuration mistakes

A few patterns I see repeatedly that defeat the purpose of origin locking:

  • Allowing * as origin — this disables the protection entirely. If you need this, you have an architecture problem, not a configuration problem.
  • Using one key for everything — a single leak compromises your entire setup. Granular keys cost nothing and contain blast radius.
  • Forgetting http:// vs https:// — these are different origins. If you serve both, allowlist both explicitly.
  • Relying on origin locking alone for paid features — anything that costs you money per call needs server-side validation, not just header checks.
  • Not rotating after a known leak — once a key is exposed publicly, rotate it. Origin locking will block most abuse but determined attackers will probe for misconfigurations.

A practical checklist before shipping

Before pushing any screenshot integration to production, verify:

  • Browser-facing keys are separate from backend keys
  • Each key has explicit origin allowlists, no wildcards in the apex domain
  • Daily request caps are set below 2x your expected peak
  • Sensitive URLs (invoices, private dashboards) go through signed server endpoints
  • Your error monitoring scrubs API keys from logged URLs
  • You have an alert configured for unusual usage patterns
  • You know how to rotate keys quickly if something leaks

Get this right once, build it into your project template, and you'll never have to worry about a 3am bill alert from a leaked key.

Want to try origin-locked screenshot keys without setting up infrastructure? PxShot's free tier gives you 100 screenshots per month with full origin restrictions, per-key usage caps, and PNG/JPEG/WebP/PDF output — enough to wire up secure OG image generation or PDF exports before committing to anything.