AWS Builder Center
What Is CORS? A Beginner's Guide with Amazon API Gateway

What Is CORS? A Beginner's Guide with Amazon API Gateway

Learn what CORS is, why your API works in curl but fails in the browser, how preflight requests work, and how to fix CORS errors in Amazon API Gateway.

Series: New To AWS Beginners Guide (12 articles)

  1. …
  2. 11
    What Is CORS? A Beginner's Guide with Amazon API Gateway This article
Developer Relations Engineer
Your API works in the terminal. It returns lovely JSON. You start drafting a new LinkedIn headline: Backend Engineer.
Then you call the same API from your website.
Blocked by CORS policy.
You wrote the website. You wrote the API. Nobody introduced them.
CORS, or cross-origin resource sharing, lets a server tell a browser which other origins may read its responses. The server supplies permission through HTTP headers; the browser enforces it. Some requests also need a permission check, called a preflight, before the browser sends them.  How CORS works 
We'll follow a concert-ticket website from a failed request to a readable booking confirmation, then turn it into a small AWS project. No real tickets. The band is called The Missing Headers, and their tour keeps getting stuck in preflight.

Why does the browser need CORS?

Imagine you're signed in to your email in one tab. In another, you visit a website promising to calculate your personality from your favorite potato.
That second website shouldn't be able to read your inbox.
The browser's same-origin policy is what stops it. It restricts how a page's scripts interact with resources from another origin. CORS gives servers a way to allow specific sharing across that boundary.  Same-origin policy 
Think of the browser as a receptionist handling replies for different tenants. A package arriving at the building doesn't mean every tenant gets to open it. The API needs to say which tenant may read the reply.
That's useful when our ticket page runs on a laptop and its API runs on AWS. They need to cooperate, but the browser can't infer permission from the fact that both were built by you, in the same hoodie.

What is an origin? Scheme, hostname, and port explained

For the web URLs we're using, an origin has three parts:
http://localhost:5173
└─┬─┘ └───┬───┘ └┬┘
scheme hostname port
The scheme is  http  or  https . The hostname identifies the host. The port identifies the network endpoint on that host.

Compare these with  http://localhost:5173 :
URL
Same origin?
Why?
 http://localhost:5173/events 
Yes
Only the path changed
 http://localhost:5173/tickets?quantity=2 
Yes
Paths and query strings aren't part of the origin
 http://localhost:3000 
No
Different port
 https://localhost:5173 
No
Different scheme
 http://127.0.0.1:5173 
No
Different hostname, even on the same computer
 https://api.example.com 
No
Different scheme, hostname, and effective port
Ports can be implicit: HTTPS defaults to  443 , so  https://api.example.com  and  https://api.example.com:443  have the same origin.  Origin definition 
For our lab, the allowed origin is  http://localhost:5173 , character for character. No  /tickets . No trailing slash. The browser is comparing an address, not accepting directions like "third door past the merch table."

Why does an API work with curl but fail in the browser?

A terminal client such as  curl  doesn't enforce the browser's CORS policy. A working terminal call tells you the API runs and returns data. It tells you nothing about whether a website can read that data.
For a cross-origin  fetch()  using its default CORS mode, the browser sends an  Origin  header. The API can permit that origin with a response header:
Access-Control-Allow-Origin: http://localhost:5173
A header is a small piece of metadata sent with an HTTP request or response. This one says: "The page running at that origin may read this response." Adding this header to your frontend request can't grant that permission. That would be like writing your own backstage pass in crayon.  Access-Control-Allow-Origin 

The request reaching the server and the page being allowed to read the reply are two different events.
For some requests, the API can run and return a response even when JavaScript can't read it. A CORS error doesn't prove that nothing happened on the server. If that request was a ticket order, you may now own a booking you've never seen.  CORS response checks 

What is a CORS preflight request?

A preflight is an automatic browser request asking whether a particular cross-origin request is allowed. Its HTTP method is  OPTIONS . You write the actual  fetch()  call; the browser handles this preliminary exchange.  Preflight requests 
It's the browser calling the venue ahead of time: "Can this website bring a POST request and a JSON body?" The order waits outside while the browser asks.
 api.example.com  is a placeholder in this example; replace it with your deployed API's base URL when you build the lab. Run the code from the locally served ticket page.
async function bookFakeTicket() {
const response = await fetch("https://api.example.com/tickets", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
eventId: "missing-headers-live",
quantity: 1,
}),
});

if (!response.ok) {
throw new Error(`Booking failed: HTTP ${response.status}`);
}

const booking = await response.json();
console.log(booking.confirmation);
}

bookFakeTicket().catch(console.error);
 JSON.stringify()  turns the order into text.  Content-Type: application/json  describes that text's format. For this cross-origin request, that content type triggers preflight. A plain  GET  without extra headers often skips it. Some  POST  requests skip it too: the trigger is the request's method and headers, not whether it changes data.  Requests that require preflight 

Step 1: OPTIONS asks permission to send the order

The relevant request headers look like this:
OPTIONS /tickets HTTP/1.1
Host: api.example.com
Origin: http://localhost:5173
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type
There's no ticket order in this request. It names the method and header the browser wants to use next.
A successful preflight response could be:
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: http://localhost:5173
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: content-type
The browser checks the response against its request.  Access-Control-Request-Headers  asks;  Access-Control-Allow-Headers  answers. Header names are case-insensitive.  Preflight request headers 

Step 2: POST sends the order, and its response gets checked too

After preflight succeeds, the browser sends the JSON order. The actual response still needs to allow the page's origin:
HTTP/1.1 201 Created
Content-Type: application/json
Access-Control-Allow-Origin: http://localhost:5173

{"confirmation":"FAKE-ENCORE-001"}
Now the page can read and display the confirmation.

If preflight fails, the browser doesn't send this POST. If preflight passes but the actual response fails CORS, the order may have run while its confirmation remains unreadable.
A successful preflight is permission to proceed, not a promise that the order will succeed. The backend can still reject an invalid quantity or return an error.  Preflight and actual-response requirements 

How to configure CORS in Amazon API Gateway

To fix a CORS error in an API Gateway HTTP API, add your website's exact origin, plus the methods and headers it sends, to the API's CORS configuration. API Gateway then answers preflight requests and adds the CORS headers for you.
We'll use an HTTP API in Amazon API Gateway. That's an API Gateway product type; its CORS setup differs from a REST API.
For the ticket lab, configure these values in the HTTP API's CORS settings:
Setting
Lab value
Purpose
Allowed origins
 http://localhost:5173 
Your local ticket page
Allowed methods
 GET ,  POST 
Read events and submit a fake booking
Allowed headers
 content-type 
Send the JSON order
Allow credentials
Off
This first lab doesn't use cookies
Max age
 0  while experimenting
Avoid reusing preflight permission during the exercise
These correspond to the API's  CORS configuration fields .
With CORS configured, API Gateway answers preflight requests and adds the configured CORS headers to integration responses. It ignores CORS headers supplied by the backend integration, so manage this HTTP API's policy in Gateway.  HTTP API CORS configuration 
If you later protect  POST /tickets  with an authorizer and send an  Authorization  header, add  authorization  to allowed headers. Preflight names that header; it doesn't carry the bearer token.
For later: an authorizer on a  $default  route can catch preflights. AWS documents adding an unauthenticated  OPTIONS /{proxy+}  route with an integration for that configuration. Our first lab uses explicit routes.  The $default route exception 

What if I created a REST API instead?

For a REST API with Lambda proxy integration, the backend must supply CORS headers on its actual responses, and preflight handling must also be configured. Setting up an  OPTIONS  response alone won't make the booking response readable. Include appropriate CORS headers on error responses too.  REST API CORS guidance 
Check the API type before following instructions. "It's an API" is about as specific as telling a mechanic you own "a vehicle."

Common CORS mistakes beginners make

Does CORS replace authentication?

No. An allowed origin says which website may read a response in the browser. It doesn't prove who's using the API or which records they may access. Other clients can call the API directly.
For real bookings, authentication establishes the user, and authorization decides what they may do. CORS doesn't check tickets at the door. It governs which browser page gets to read the venue's reply.  AWS explanation of CORS 
Our lab returns fake confirmations without sign-in on purpose. There's no payment, inventory, or personal data to protect in this exercise.

Can I fix CORS with mode: "no-cors"?

For an API whose JSON you need to read, no.
 no-cors  gives you an opaque response: JavaScript can't access its body or headers. It also restricts the request you can send. You've traded the CORS error for a sealed envelope. Congratulations on your envelope.  Fetch request modes 

Should I allow every origin with an asterisk?

 Access-Control-Allow-Origin: *  can be appropriate for public data meant to be readable by any website. For this lab, use the exact origin so you can observe the matching rule.
If you later use  credentials: "include"  for cross-origin cookies, the server must return an explicit allowed origin and  Access-Control-Allow-Credentials: true . The wildcard won't work for that credentials mode. Cookie policies still apply.  Credentialed CORS requests 
Don't copy whatever  Origin  a request sends straight into  Access-Control-Allow-Origin . Check it against your allowed list. A guest list loses its purpose if the guests can rewrite it.

How to debug a CORS error in the browser

Open your browser's developer tools, select Network, and click the booking button once. The console tells you there's a problem; Network helps you find the failed exchange.
What you observe
What to inspect next
 OPTIONS , but no  POST 
Preflight status and allowed origin, method, and headers
 POST  ran, but the page can't read its response
CORS headers on the actual response, including errors
Request originates from port  3000 , policy allows  5173 
The exact page origin; check  window.location.origin 
 401 ,  403 , or  500 
Authentication, authorization, or backend failure, as well as CORS
No OPTIONS this time
The request might skip preflight, or permission might be cached
Preflight permission has its own cache. For this exercise, use max age  0  and a fresh browser session if an earlier successful check makes the behavior confusing.  Preflight caching 
You can also inspect the server's preflight response with  curl . Replace the base URL; include the stage path if your API's invoke URL has one.
API_BASE='https://YOUR_API_ID.execute-api.YOUR_REGION.amazonaws.com'
curl -i -X OPTIONS "$API_BASE/tickets" \
-H 'Origin: http://localhost:5173' \
-H 'Access-Control-Request-Method: POST' \
-H 'Access-Control-Request-Headers: content-type'
For HTTP API CORS handling, AWS requires  Origin  and  Access-Control-Request-Method  on that OPTIONS request.  Required preflight headers 
Read the headers, then verify in a real browser.  curl  can show you the permission slip; it doesn't play the receptionist.

Build a CORS ticket-booking lab with the new AWS experience

Build a one-page concert site with an event listing and a Book a fake ticket button. A successful click should display  FAKE-ENCORE-001 , which admits you to zero concerts.

API Gateway receives and routes the HTTP requests. Lambda runs the code that returns the events and fake confirmation. Keep the frontend local so the two different origins are easy to see.
Start with the  new AWS experience  and create a learning project, or use an existing learning project. My  new-to-AWS walkthrough  covers getting started.
As of October 2026, AWS documents a limited rollout of the new experience to new customers. API Gateway and Lambda are supported; use your project's assigned Region and check its plan and permissions. Service availability doesn't make every resource free.  New experience and supported services 

Give your AI coding assistant these prompts

Connect Agent Toolkit for AWS to a compatible coding assistant using the  official setup instructions . The toolkit supplies AWS documentation, skills, and tools for working with your AWS resources. It helps during development; it isn't another service your ticket requests pass through.  Agent Toolkit overview 
Start with the build:
Help me build a beginner CORS lab using Agent Toolkit for AWS and
current AWS documentation. Explain each step as we go.

Use my learning project in the new AWS experience. Confirm the project,
assigned Region, supported services, and permissions. Explain the
resources and expected costs before deployment.

Create a local concert-ticket page at http://localhost:5173 and an
Amazon API Gateway HTTP API with explicit GET /events and POST /tickets
routes backed by Lambda. Return made-up events and fake confirmations.
Use JSON for POST. No real purchases, personal data, database, or sign-in.
Label the button "Book a fake ticket".

Start with CORS unconfigured. Serve the frontend through a local web
server, and call the AWS API directly without a development proxy.
Manage AWS resources as infrastructure as code and include cleanup.
Keep AWS credentials out of browser code.
Then ask it to help you investigate:
Before fixing CORS, show me the JSON POST working from the terminal.
Then help me try it from the page and inspect OPTIONS in browser
Network tools. Verify that this failed preflight prevents POST.

Configure HTTP API CORS for exactly http://localhost:5173, GET and POST,
content-type, no credentials, and max age 0 while testing.
Repeat the browser request. Show me OPTIONS, POST, the response CORS
header, and the fake confirmation displayed by the page.

Serve the same page on port 5174 and call the same API. Explain why
that origin fails. Don't change the allowed list to make it pass.
Report what we actually observed and what we haven't tested.
Finally, ask it to help you finish:
Give me a short explanation of why CORS doesn't authenticate a user.
Then walk me through deleting this lab's resources, including leftover
logs or deployment artifacts, and verify what's still present.
Your finish line: port 5173 gets a readable fake confirmation; port 5174 fails preflight; you can point to the headers that explain both.
Build it, break it on purpose, and post what you learned on Builder Center. The Missing Headers have been stuck in preflight since the first section. You can get them on stage.

Series: New To AWS Beginners Guide (12 articles)

  1. …
  2. 11
    What Is CORS? A Beginner's Guide with Amazon API Gateway This article
Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article