ZeDaq.ai
Shopper using virtual try-on for a tailored suit

Keeping API Keys Out of the Browser: A Proxy Pattern

By the ZeDaq.ai team · · 4 min read

On this page
  1. Why can't you put the key in front-end code?
  2. How does a proxy pattern work?
  3. What does a minimal proxy look like?
  4. What should the proxy check before forwarding?
  5. Can you use a serverless function?
  6. How do you handle polling and errors?
  7. What other protections help?

To keep an API key out of the browser, send the browser's request to your own backend endpoint. That endpoint, called a proxy, adds the key on the server, checks the request and forwards it to the API. The key never appears in page source or network traffic.

This guide is for developers calling a try-on API from a storefront. It builds on the overview of the virtual try-on API for ecommerce developers. It is general engineering guidance, not security certification.

Why can't you put the key in front-end code?

Anything the browser can read, a visitor can read. That includes JavaScript bundles, page source and network requests. A key in front-end code is public, even if it looks hidden.

For ZeDaq.ai, the API reference separates server keys, which should stay on your server, from the widget key used in the script tag. The docs say widget keys are visible in page source, so they must be locked to your domain, and unlocked widget keys are refused.

So there are two safe options. Use the widget with a domain-locked key, or call the REST API from your own server. This article covers the second.

How does a proxy pattern work?

The flow has four steps:

  1. The browser sends a request to your endpoint, such as /api/tryon.
  2. Your server checks that the request is allowed.
  3. Your server calls POST /api/v1/tryon with the x-api-key header and the key from a secret store.
  4. Your server returns the result to the browser.

The browser never sees the key, and it never talks to the third-party API directly.

What does a minimal proxy look like?

Here is a short example in Node.js. It is a sketch, so add your own validation and error handling.

// POST /api/tryon on your server
export async function POST(req) {
  const { personImage, productImage, garmentType } = await req.json();

  const res = await fetch(`${process.env.ZEDAQ_API_BASE}/api/v1/tryon`, {
    method: "POST",
    headers: {
      "content-type": "application/json",
      "x-api-key": process.env.ZEDAQ_API_KEY,
    },
    body: JSON.stringify({
      person_image_base64: personImage,
      product_image_base64: productImage,
      garment_type: garmentType,
    }),
  });

  return new Response(res.body, { status: res.status });
}

The field names come from the API reference. A 202 response means the render is still running, so your proxy should also expose a way to poll the poll_url it returns.

Set ZEDAQ_API_BASE to the base URL given in the API reference. Store the key as an environment variable or in your host's secret manager. Never commit it to a repository.

What should the proxy check before forwarding?

A proxy that forwards everything is just a new open door. Add these checks:

  • Origin: accept requests only from your own site.
  • Input: allow only the fields you expect, and limit image size.
  • Garment type: accept only upper_body, lower_body or dresses.
  • Rate limiting: limit requests per visitor or session so one person can't use up your monthly allowance.
  • Product source: use a product image from your own catalog, not a URL the visitor supplies.

The last point stops someone from using your account to render arbitrary images. For the header details, read using an x-api-key header safely.

Can you use a serverless function?

Yes. A serverless function suits this job, since the proxy is small and stateless. The same code can run on most hosts that support server-side functions, with the key in the host's secrets.

Keep it simple: one function for the try-on call and one for polling. Don't log request bodies, because they contain shopper photos.

How do you handle polling and errors?

The API reference lists error codes such as missing_api_key, invalid_api_key, domain_not_allowed and tryon_cap_reached. Don't pass these to shoppers word for word.

Map them to friendly messages, and log the code on the server. For example, show "Try-on is unavailable right now" to the shopper, and alert yourself when you see invalid_api_key or tryon_cap_reached.

For a render that returns a 202, give the browser a second proxy route, such as /api/tryon/status/[jobId]. That route calls the poll_url from your server with the same header, so the key stays private during polling too. The reference suggests polling every 2 seconds.

What other protections help?

Layer a few more defenses:

  • Rotate keys. If a key leaks, revoke it and create a new one under Dashboard → API keys.
  • Lock keys to domains. The docs say you can lock a key so requests from other origins are refused.
  • Watch usage. Unexpected jumps mean something is wrong.
  • Cache carefully. Reusing results saves calls, but keep photos out of the cache, as in how to cache try-on results without keeping photos too long.

Good guides on sizing data also help you decide what to expose. For example, how to write size guides that reduce returns covers what shoppers need to see.

Call the try-on API from your own server

FAQs

Is obfuscating the key in JavaScript enough?

No. Anything shipped to the browser can be read. Keep the key on the server.

Can I use the widget instead of a proxy?

Yes. The widget uses a key that is visible in page source, so the docs require it to be locked to your domain.

Where should I store the API key?

In an environment variable or your host's secret manager, never in the repository or front-end code.

What if my key leaks?

Revoke it in the dashboard and create a new one. Then update your server's secret.

Try ZeDaq.ai free

Virtual try-on and Find my size for your store, plus client, order and invoicing tools for tailors, in one subscription.

Related articles