While setting up a new application with some frameworks I hadn’t done too much work with I came across an old friend CORS. After some debugging and tweaks I got everything up and running, but not without some gotchas. I want to share those with you and what I did to solve them.
Cross-Origin Resource Sharing (CORS) is a security feature enforced by web browsers that restricts how resources on a web server can be accessed from a different domain. If not configured properly, CORS issues can prevent frontend applications from making API requests, leading to frustrating errors.
This is what I was using to build the application.
Frontend: Astro, hosted at example.com
Backend: Hono, hosted at api.example.com
Infrastructure: SST with API Gateway V2
Before jumping right into the solutions, I want to run through common CORS-related problems developers encounter:
Wildcard (*
) Usage with Credentials:
Browsers block requests if Access-Control-Allow-Origin
is set to *
while Access-Control-Allow-Credentials
is true
.
Missing or Incorrect Headers:
Requests may fail if required headers like Authorization
or Content-Type
are not explicitly allowed.
Improper Handling of Preflight Requests:
Some APIs do not handle OPTIONS
requests properly, leading to failed cross-origin requests.
Now for the good stuff…
To enable CORS correctly in SST's API Gateway setup, we need to configure the cors
settings explicitly.
In your SST configuration, define the cors
settings for API Gateway V2:
const exampleApiGateway = new sst.aws.ApiGatewayV2("ExampleApiGateway", {
domain: "api.example.com",
cors: {
allowOrigins: ["https://example.com"],
allowMethods: ["GET", "POST", "PATCH", "OPTIONS"],
allowHeaders: ["Content-Type", "Authorization"],
allowCredentials: true,
},
});
Allow specific origins: Avoid using *
when allowCredentials
is true
.
Explicitly define methods: Ensure OPTIONS
is included to handle preflight requests.
Set required headers: Include headers like Authorization
and Content-Type
.
With this setup, API Gateway will return the correct CORS headers, but we also need to handle CORS within Hono to properly support authentication.
In addition to configuring API Gateway, we need to explicitly handle CORS within our Hono backend. There are 2 ways to do this and I tested both. Starting with the first example, where I used my own middleware to the cors middleware provided by Hono.
To correctly handle CORS in Hono, add middleware to set the appropriate headers and respond to preflight (OPTIONS
) requests:
import { Hono } from 'hono';
import { cors } from 'hono/cors';
// --- Example 1 ---
const app = new Hono();
app.use('*', async (c, next) => {
c.header('Access-Control-Allow-Origin', 'https://example.com');
c.header('Access-Control-Allow-Methods', 'GET, POST, PATCH, OPTIONS');
c.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
c.header('Access-Control-Allow-Credentials', 'true');
if (c.req.method === 'OPTIONS') {
return new Response(null, { status: 204 });
}
await next();
});
// --- Example 2 ---
const allowedOrigins = ['https://arclly.com', 'https://dev.arclly.com'];
app.use(
'*',
cors({
origin: (origin) =>
allowedOrigins.includes(origin) ? origin : 'https://arclly.com',
credentials: true, // Required to send cookies
})
);
Match API Gateway settings: Ensure origins, methods, and headers match those defined in API Gateway.
Handle preflight requests: Return a 204 No Content
response for OPTIONS
requests.
Allow credentials: Set Access-Control-Allow-Credentials
to true
for authentication.
With this all set up, CORS should work correctly for both simple and preflighted requests.
A little bonus for helping test your CORS Configuration.
To verify that CORS is working correctly, use curl
to test preflight requests:
curl -i -X OPTIONS https://api.example.com/users/me \
-H "Origin: https://example.com" \
-H "Access-Control-Request-Method: PATCH" \
-H "Access-Control-Request-Headers: Content-Type, Authorization"
The response should include the correct CORS headers:
HTTP/2 204
access-control-allow-origin: https://example.com
access-control-allow-methods: GET, POST, PATCH, OPTIONS
access-control-allow-headers: authorization,content-type
access-control-allow-credentials: true
If you see any errors, double-check that API Gateway and Hono configurations are aligned.
Configuring CORS properly is important when working with separate frontend and backend domains. By ensuring that API Gateway and Hono handle CORS correctly, you can avoid common pitfalls and allow authenticated requests to work seamlessly.
Key takeaways:
Define explicit CORS settings in API Gateway.
Add CORS middleware in Hono to handle preflight requests.
Test using curl or browser DevTools to confirm proper configuration.
With these steps, your API should now be accessible from your frontend without any CORS-related issues.
Hope this helps you avoid CORS issues.
Happy Coding!