Home
/Avoiding CORS Issues with Hono, SST, and API Gateway
Avoiding CORS Issues with Hono, SST, and API Gateway
David Kennedy
·
2/24/2025

Introduction

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.

What is CORS?

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.

Background

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

Common CORS Pitfalls

Before jumping right into the solutions, I want to run through common CORS-related problems developers encounter:

  1. Wildcard (*) Usage with Credentials:

    • Browsers block requests if Access-Control-Allow-Origin is set to * while Access-Control-Allow-Credentials is true.

  2. Missing or Incorrect Headers:

    • Requests may fail if required headers like Authorization or Content-Type are not explicitly allowed.

  3. Improper Handling of Preflight Requests:

    • Some APIs do not handle OPTIONS requests properly, leading to failed cross-origin requests.

Now for the good stuff…

Configuring API Gateway for CORS

To enable CORS correctly in SST's API Gateway setup, we need to configure the cors settings explicitly.

Correct API Gateway CORS Configuration

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,
  },
});

Key Considerations:

  • 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.

Configuring Hono for CORS

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.

Adding CORS Middleware in 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
  })
);

Key Considerations:

  • 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.

Testing CORS Configuration

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.

Conclusion

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!

Powered by TinyCM