TidyURLs
Go to Tools
BLOG
The TidyURLsJournal

Practical guides, tutorials, and insights about URLs, SEO, web development, and online marketing. Learn, troubleshoot, and get the most of the web.

Back to the Journal
TidyURLs Journal

Why Your API Works in Postman but Fails in the Browser

Practical insights, clear explanations, and useful ideas for understanding the web.

October 25, 20267 min read6 topics
Why Your API Works in Postman but Fails in the Browser
Explore the details
#CORS#API troubleshooting#Fetch API#browser security#HTTP#web development
October 25, 20267 min read

An API request can return the expected result in Postman and still fail when your website calls it. That does not necessarily mean the endpoint is broken: Postman and browsers follow different security rules, send different request details, and report failures differently. The most common cause is Cross-Origin Resource Sharing (CORS), but credentials, preflight requests, HTTPS, and request headers can also be involved.

This guide explains how to identify the difference, inspect the request the browser actually sends, and fix the server-side configuration without weakening browser security.

Key takeaways

  • Postman is not subject to the browser's same-origin policy, so a successful Postman request does not prove a browser request is allowed.
  • A browser may send an OPTIONS preflight before the actual request. The server must handle it and allow the requested method and headers.
  • Credentialed requests need explicit CORS configuration; Access-Control-Allow-Origin: * cannot be used with credentials.
  • HTTPS pages can be blocked from making requests to insecure HTTP endpoints as mixed content.
  • A CORS error can hide a response from JavaScript even when the server received and processed the request.

Why an API works in Postman but not in the browser

Browsers enforce the same-origin policy. An origin is the combination of a URL's scheme, host, and port. For example, https://app.example.test and https://api.example.test are different origins, as are http://localhost:3000 and http://localhost:8000.

When browser JavaScript makes a cross-origin request with fetch() or XMLHttpRequest, the browser applies CORS rules. The API server must return response headers that permit the requesting origin and, when applicable, its method, headers, and credentials. If the browser does not receive the required permission, it prevents JavaScript from reading the response.

Postman is a desktop API client, not a webpage running under your site's origin. It can send requests without the browser's CORS enforcement. That makes it useful for checking whether an endpoint responds, but it does not reproduce the browser's security checks.

A failed browser request can mean different things:

  • The browser never sent the request because of a preflight or mixed-content restriction.
  • The server received the request but did not return CORS headers the browser accepts.
  • The server returned an application error, such as a 401 or 500.
  • The response reached the browser, but JavaScript is not allowed to read it.

Use the browser's Developer Tools Network panel and Console together to distinguish these cases. A status code alone is not enough to diagnose a CORS failure.

Check the browser request before changing code

Open Developer Tools, select Network, and reproduce the issue. Inspect both the request and its response. If the request is absent, look for a preflight failure, mixed-content warning, or other browser-side block. If it appears, check its URL, method, request headers, status, and response headers.

Compare the browser request with the working Postman request. Check for differences such as:

  • A different base URL, scheme, host, or port.
  • Missing or differently formatted authorization headers.
  • A JSON content type or custom header that triggers preflight.
  • Cookies that Postman sends but the browser does not, or vice versa.
  • A redirect to a different origin or from HTTPS to HTTP.

A HTTP status checker can help inspect the status of an endpoint that it can reach, but it does not run in your page's browser context. It cannot establish that the browser's origin, cookies, preflight, or CORS permissions are correct.

Understand CORS preflight requests

Some cross-origin requests cause the browser to send a preflight request first. This is an OPTIONS request asking whether the server permits the upcoming request. A preflight is commonly triggered by methods other than GET, HEAD, or POST, by certain non-safelisted request headers, or by a content type such as application/json.

The preflight includes headers such as Origin, Access-Control-Request-Method, and, when relevant, Access-Control-Request-Headers. The server needs to respond with suitable CORS permissions. For example, it may allow the requesting origin, the intended method, and the headers the browser plans to send. The browser sends the actual request only if the preflight succeeds.

You can inspect an authorized endpoint's preflight behavior with a command like this, replacing the example URL and origin with your own:

curl -i -X OPTIONS 'https://api.example.test/v1/items' \
  -H 'Origin: https://app.example.test' \
  -H 'Access-Control-Request-Method: POST' \
  -H 'Access-Control-Request-Headers: authorization,content-type'

This shows what the server returns to that command. It does not make curl enforce browser CORS rules, so a successful command is not proof that the browser will accept the response. Check that the response permits the specific origin, method, and requested headers, and that the server or gateway handles OPTIONS rather than rejecting it or redirecting unexpectedly.

Configure credentials and headers deliberately

For a request that uses cookies or other browser credentials, the frontend must opt in, for example with credentials: "include" in fetch(). The server must also return Access-Control-Allow-Credentials: true and an explicit allowed origin, such as https://app.example.test. It cannot use Access-Control-Allow-Origin: * for a credentialed CORS response.

The server should allow only the origins and request details the application needs. Do not reflect any incoming Origin value without validating it. CORS is not a substitute for authentication, authorization, CSRF protections, or server-side input validation.

Here's a minimal browser-side request for an API that is configured to permit the page's origin and the Authorization header:

async function loadItems() {
  const response = await fetch("https://api.example.test/v1/items", {
    headers: {
      Authorization: "Bearer YOUR_ACCESS_TOKEN"
    }
  });

  if (!response.ok) {
    throw new Error(`API returned HTTP ${response.status}`);
  }

  return response.json();
}

The placeholder is not a real credential. In a real application, handle tokens securely and avoid embedding sensitive secrets in frontend code. An Authorization header generally causes a preflight, so the API must allow that header as well as the request method. If the API returns a response header that frontend code needs to read, that header may also need to be exposed using Access-Control-Expose-Headers.

Check HTTPS and mixed content

If your page is loaded over HTTPS and tries to fetch an API over plain HTTP, the browser may block the request as mixed content. Postman may still reach the HTTP endpoint because it is not enforcing the page's mixed-content policy.

Use an HTTPS API endpoint for an HTTPS site, and check redirects too: a request that starts on HTTPS but is redirected to an insecure URL can still cause problems. Browser Console messages and the Network panel usually identify mixed content explicitly. CORS headers do not make an insecure HTTP request acceptable to an HTTPS page.

A practical troubleshooting workflow

  1. Read the browser Console error. Note whether it reports CORS, mixed content, a failed preflight, a certificate problem, or an HTTP error.
  2. Inspect Network activity. Look for an OPTIONS request and determine whether the actual request followed it.
  3. Compare request details. Match the URL, method, origin, headers, and credentials against the working Postman request.
  4. Inspect the server response. Check the status and CORS headers on both the preflight and actual response. If a request is credentialed, verify the explicit origin and credentials permission.
  5. Check all response paths. CORS headers may need to be present on error responses as well as successful responses. A proxy, load balancer, or authentication layer may handle OPTIONS differently from the application.
  6. Retest in the browser. After changing server or gateway configuration, reproduce the request from the actual page origin. A command-line test cannot replace this step.

Common mistakes

  • Adding CORS headers only to the successful response. A 401 or 500 without the expected headers can appear to JavaScript as a CORS failure, obscuring the underlying status.
  • Allowing GET but forgetting OPTIONS. The browser's preflight is a separate request and must receive an acceptable response.
  • Allowing the method but not the headers. For example, the server may permit POST but omit authorization from its allowed headers.
  • Using a wildcard with credentials. Credentialed CORS requests require a specific allowed origin, not *.
  • Treating Postman success as proof of browser compatibility. Postman can confirm that an endpoint is reachable and responds, but it does not enforce browser origin restrictions.
  • Trying to fix CORS in frontend JavaScript. The permission is granted by the server's response. A frontend workaround cannot make a server's response pass the browser's CORS check.
  • Disabling browser security. This is not an appropriate fix. Correct the API or gateway configuration and test from the intended origin.

Conclusion

When an API works in Postman but not in the browser, compare the actual requests before assuming the API logic is at fault. Browser origin rules, preflight handling, credentials, headers, and HTTPS requirements can all change whether JavaScript is allowed to make or read a request. Use Developer Tools to identify the specific failure, then configure the server to permit only the origins and request details your application needs.

References