Mid-advanced website skills · Browser and server boundaries

How Can a Static Website Call an API Without Exposing Secrets?

A small live-data feature can fit a mostly static site. Define what the browser may know, what the server must protect, and what visitors see when the provider is unavailable.

Call an API directly from the browser only when the provider explicitly supports that public integration. Private credentials and privileged operations belong behind a properly designed server endpoint. Keep that endpoint narrow, validate its inputs and outputs, and design loading, empty, error, and timeout states before adding the happy-path display.

Which kind of API are you connecting?

  • Public read-only data: direct browser access may work if the provider supports it and permits the required cross-origin response.
  • A documented browser SDK: some providers intentionally use a publishable identifier. Follow the provider’s restrictions and intended integration model.
  • A secret-authenticated service: a private provider key belongs on a trusted server, not in shipped JavaScript.
  • User-specific or privileged actions: the server must authenticate the caller and authorize the particular operation or record.

Do not decide that a value is safe merely because it is named “public” in a sample project. Read the provider’s documentation and identify what an unauthorized person could do with it. A private repository does not hide a credential once a build includes it in HTML, JavaScript, source maps, or browser requests.

What does a useful server boundary do?

A Cloudflare Pages Function can receive a request from the site, call a fixed provider endpoint using a server-side secret binding, and return only the public fields needed by the page. The browser never needs the private provider credential. Define a small response contract, for example:

{
  "nextAvailableDate": "2026-10-12",
  "checkedAt": "2026-10-06T12:00:00Z",
  "searchThrough": "2026-11-05"
}

These are illustrative values. Define dates, time zones, and the search window in the contract. A successful response with nextAvailableDate: null can mean no slot was found within that documented window. A 503 response means the service could not provide a reliable answer; it must not be turned into “no availability.”

Example: public consultation availability

The page requests /api/availability. The function reads one provider route and returns a public next-available date plus an update timestamp. It excludes customer names, calendar details, internal IDs, and provider credentials. The page retains a normal booking link and says that displayed availability does not reserve a time.

Before exposing even a public endpoint, decide who owns its cost and abuse limits. Hiding a provider key does not stop someone repeatedly calling your intermediary. Keep credentials out of URLs and logs, and do not return raw provider errors containing private configuration.

How do you avoid creating an open proxy?

Write an explicit request and response contract. A safe small integration should not accept an arbitrary destination URL and fetch it on demand. That turns a focused feature into a much broader server-side request capability.

  1. Fix or strictly allowlist the upstream destination and permitted operation.
  2. Accept only the necessary methods and parameters, with type and size limits.
  3. Validate authorization for any private data or state-changing operation.
  4. Apply sensible timeouts, rate limits, and provider-cost controls.
  5. Validate the upstream response and return a small approved data shape.
  6. Return understandable error categories without leaking credentials or raw internals.
  7. Keep logs limited to the evidence needed to operate the service.

Cookie-authenticated state-changing endpoints also need an appropriate CSRF design; CORS alone is insufficient. Authentication, authorization, input validation, and rate limiting do different jobs. One control does not replace the others. This guide outlines a boundary, not a complete security review for accounts, medical information, payments, or other sensitive systems.

Why is CORS not the same as API security?

CORS is a browser mechanism governing access to cross-origin responses. It does not prove that a caller is authorized, and non-browser clients are not restricted by it in the same way. A server still needs its own authentication and authorization where the operation requires them.

A same-origin route such as /api/availability can simplify the browser’s request path. It does not make that route private. The function must still enforce its intended scope.

Setting fetch to mode: 'no-cors' does not unlock JSON blocked by CORS. It generally gives the browser script an opaque response it cannot inspect as normal API data. Use the provider’s supported integration or a legitimate server boundary. Credentialed cross-origin requests also have stricter origin requirements; a wildcard allowed origin is not a universal fix.

What should the browser do when the request fails?

Keep the page’s core content and ordinary next step available while data loads. Use clear states for loading, valid data, no available result, and unavailable data. “No slots available” and “Availability could not be loaded” mean different things; a provider outage must not be presented as a confirmed lack of appointments.

A fetch promise does not reject solely because the server returns a 404 or 500 status. Check response.ok or the expected status before parsing and using the response. Then validate the response shape; valid JSON may still contain the wrong fields or types.

Render untrusted strings as text rather than inserting them as HTML. Use a bounded timeout and an understandable retry control. For a read-only request, a small deliberate retry policy may be reasonable; do not make infinite retries that amplify an outage.

For state-changing operations, a browser timeout or cancellation does not prove that the server stopped processing. Automatic retries can duplicate a booking or other action unless the endpoint has an appropriate idempotency design. Keep consequential write flows out of a casual “retry fetch” helper.

When is caching an API response appropriate?

For public read-only data, decide how stale the information may be and display a meaningful update time when useful. A short shared cache can reduce repeated upstream calls, but only if the returned data is actually suitable for sharing across visitors.

Do not copy public cache headers onto personalized, authenticated, or sensitive responses. Consider how error responses, empty results, and upstream rate limits should be handled separately. A cached provider failure should not quietly become a permanent-looking business fact.

Set the operational budget before launch: expected request volume, provider limits, who checks errors, and what happens when a limit is reached. The cache diagnosis guide explains freshness concepts, but API data still needs its own privacy and correctness decisions.

What tests establish that the integration is ready?

  • Normal data, empty data, missing fields, invalid JSON, and unexpected content types
  • Controlled 429, 500, and 503 responses, slow responses, and offline behavior
  • Valid and invalid inputs, disallowed methods, and attempts to request an unapproved destination
  • Absence of private credentials from delivered assets and browser request payloads
  • Correct preview and production bindings, without sharing customer data for testing
  • A visible, truthful fallback and a working ordinary contact or booking route

Use synthetic fixtures and the provider’s approved test facilities. Follow the preview versus production checklist for environment differences. If a live-data feature is optional, apply progressive enhancement so a failed API does not make the whole site unusable.

Sources and further reading

Documentation checked October 6, 2026. Examples illustrate a workflow; they are not measured client results or guarantees.

Build on a clear website foundation

This free article extends the beginner workflow with a focused technical topic. If you need the starting process, BUILD • PUBLISH • OWN IT is a 44-page beginner PDF guide for $99.99 USD covering a ChatGPT, GitHub, and Cloudflare website workflow. Review its contents to see what the PDF includes; this article does not promise that every technique here is covered there.

Colby will personally assist if you get stuck following the guide. Additional hands-on work may carry a fee, with the scope and price agreed before work begins. Applicable tax is shown at checkout, and third-party service charges are separate. Read the purchase terms for details.