惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

GbyAI
GbyAI
Martin Fowler
Martin Fowler
I
InfoQ
腾讯CDC
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
爱范儿
爱范儿
Microsoft Security Blog
Microsoft Security Blog
Google DeepMind News
Google DeepMind News
D
DataBreaches.Net
云风的 BLOG
云风的 BLOG
F
Fortinet All Blogs
N
Netflix TechBlog - Medium
博客园 - 聂微东
Microsoft Azure Blog
Microsoft Azure Blog
D
Docker
博客园 - 三生石上(FineUI控件)
Y
Y Combinator Blog
博客园 - Franky
Engineering at Meta
Engineering at Meta
B
Blog
罗磊的独立博客
Apple Machine Learning Research
Apple Machine Learning Research
Jina AI
Jina AI
V
Visual Studio Blog

Clerk Blog

Going to production with Clerk Deploy Clerk Init: The fastest way to start a new project Introducing Clerk CLI Middleware-based route protection bypass Postmortem: Clerk System Outage (March 10, 2026) Clerk for the AI era Add API Key support to your SaaS in minutes Postmortem: Clerk System Outage (February 19, 2026) Using Clerk in a React Native app Postmortem: DNS Provider Outage (February 10, 2026) How do I implement passkeys in Next.js? Clerk ranked #4 fastest-growing software vendor on Ramp’s December 2025 list How do I handle JWT verification in Next.js? Committing to Agent Identity: Clerk raises $50m Series C from Menlo and Anthropic’s Anthology Fund What is the best way to handle authentication in Next.js App Router? Postmortem: Database Incident (September 14–18, 2025) How do I add authentication to a Next.js app? Introducing Free Trials in Clerk Billing Postmortem: August 28, 2025 - elevated API latency and errors Introducing Mosaic: Bring Your Brand to Every Authentication Flow Multi-tenant authentication: What you need to know (and how Clerk helps) What are the risks and challenges of multi-tenancy? Resilience in Practice: Regional Failover at Clerk Build a Cross-Platform B2B App with Clerk, Expo, and Supabase Highlights from the MiduDev/Clerk Hackathon Add multi-tenancy to an app built with Clerk, Lovable, and Supabase How to build an AI coding rules app with Clerk, Lovable, and Supabase How to Build Multi-Tenant Authentication with Clerk Choosing the right SaaS architecture: Multi-Tenant vs. Single-Tenant Postmortem: June 26, 2025 service outage
How to skip CORS preflights and speed up your API with po...
Colin Sidoti · 2022-04-30 · via Clerk Blog

At Clerk, we have an API that is directly accessible from the frontend (we call it the Frontend API). It exclusively handles cross-origin requests, but none of those requests trigger a CORS preflight.

This is by design. CORS preflights do not add security for modern applications and they add an extra network round-trip, so we made sure that every API request is considered a "simple request."

What do you mean CORS preflights do not add security?

It's a common misconception that CORS preflight requests add security to modern applications. Why else would they exist?

Surprisingly, CORS preflights exist to protect old applications, not new ones.

Specifically, the CORS designers were concerned about old applications that incorrectly assumed that browsers would never allow request methods besides GET or POST, or would never allow custom HTTP headers.

When browsers added the capability to send alternative request methods and custom headers via fetch (and its older sibling, XMLHttpRequest), suddenly applications that made this assumption were at risk.

To mitigate the risk to old applications, an extra "preflight" request was added to requests with PATCH, PUT, DELETE methods, and to requests with custom headers. The idea is that, if those applications fail to respond to the preflight in a very specific way, then the actual request will never be dispatched.

It's dirty and it adds latency, but it works.

The annoying part is: modern applications that anticipate PATCH, PUT, DELETE requests and custom headers don't gain any security from CORS preflights, it's just extra latency they need to incur to protect legacy applications. In 2022, it's like robbing Peter to pay an exceptionally stubborn Paul who won't update their decades old codebase, but we digress...

How can CORS preflights be skipped?

Certain cross-origin requests are classified as "simple requests" and do not require a successful preflight before being dispatched.

Based on the section above, it might be easy to guess which requests qualify as simple: GET or POST requests without custom headers. (Note: This is a slight simplification, the full details are available on MDN.)

To build an API that doesn't trigger preflights, we need to design polyfills for modern request methods and custom headers.

One critical point first

The polyfills below assume you have configured your CORS middleware to outright reject requests that should not be processed.

As an example, consider CORS middleware running on api.example.com that is configured to allow the Origin of https://www.example.com.

Now, consider a request comes in with the Origin of https://randomattacker.com

Does your CORS middleware reject this request, or does it allow the request to be processed?

It depends on your middleware.

Some middleware might simply add an access-control header (below), then allow the request to continue:

This header doesn't stop the request from being processed, but it does stop the browser from reading your server's response.

This is not the behavior you want.

Instead, you want your middleware compare the received Origin to the allowed Origin, and immediately cancel the request if they don't match.

The only way to confirm your middleware's behavior is to write your own tests. Clerk needed to write our own middleware to reject requests with undesirable CORS options (origin, credentials, etc).

Polyfilling request methods

Polyfilling the request method is trivial - and we were fortunate to have inspiration from Ruby on Rails. Every mutation request to our frontend API is dispatched as a POST, but the method can be overridden using a query string like ?_method=PATCH. In our backend, we run middleware to ensure that the request is treated as a PATCH when this query string is present.

Custom headers can be more challenging to polyfill. It really depends on what type of content you're putting in the header:

  • If the header does not contain sensitive information, the polyfill can use the same query string approach used above for request methods
  • If the header does contain sensitive information, the polyfill should be handled within the the request body

Typically, developers want to customize two headers:

  1. Content-Type: To set it to application/json. This is not sensitive and can be polyfilled using the query string.
  2. Authorization: To include an access token. This is sensitive and should not be polyfilled using the query string.

It's important that sensitive information is not added to the query string because the request path is often logged to tools like bug trackers and analytics software. To obscure this information from those tools, it's better to add the field to the request body. In the case of the Authorization header, an extra form value or JSON attribute will suffice.

That's everything!

These simple changes will eliminate CORS preflight requests from a frontend talking to a frontend API. In the process, it eliminates a round trip, which can easily take over 100ms if your user is geographically far from your server. Even in the best case of edge computing, this strategy will likely shave off ~20ms from your overall response time.

For the modern web, every millisecond counts!