Over the course of implementing stricter API protection for a project using custom headers, Cloudflare, and Feathers.js, I went deep into how CORS, preflight requests, and custom headers work — and where performance can unexpectedly take a hit.
This post summarizes what I learned.
Any non-simple request will trigger a preflight. In my case:
Cross-origin (main site calls a subdomain API)
Uses Content-Type: application/json
Uses custom headers like ss-cleanup
or x-dnxclient-id
Therefore, every request results in:
An OPTIONS request
A full GET/POST request if preflight succeeds
Must satisfy:
Method: GET
, POST
, or HEAD
Content-Type: application/x-www-form-urlencoded
, multipart/form-data
, or text/plain
No custom headers
My headers looked like this:
headers: {
"content-type": "application/json",
"ss-cleanup": localStorage.getItem("SS-VENDOR_CLEANUP"),
"x-dnxclient-id": generateNoise()
}
Which meant every fetch sent a preflight, costing ~100–300ms per request, even when calling between subdomains.
To challenge non-browser bots, I used a custom header (ss-cleanup
) that gets injected via real user interaction (mousedown, scroll, etc).
Cloudflare rule:
(starts_with(http.request.uri.path, "/dnxapi/meets")
and not len(http.request.headers["ss-cleanup"]) > 0
and http.request.method ne "OPTIONS")
Does not block browser preflight requests
Blocks or challenges bots skipping JS
Cleanly integrates with your frontend
const allowedOrigins = new Set([
"https://example.com",
"https://api.example.com",
"https://community.example.com",
"https://stg.example.com",
"http://localhost:3000"
]);
app.use(cors({
origin: function (origin, callback) {
if (!origin || allowedOrigins.has(origin)) {
callback(null, true);
} else {
callback(new Error("Not allowed by CORS"));
}
},
credentials: true,
allowedHeaders: [
"Content-Type",
"Authorization",
"ss-cleanup", // Custom validation header
"x-dnxclient-id" // Noise header to prevent silent failures
],
maxAge: 600, // Cache preflight result for 10 minutes
}));
I migrated from:
example.com (Next.js)
↘ fetch https://api.example.com
To:
example.com (Next.js + Apache)
↘ proxy /dnxapi/* → http://localhost:8888/*
✅ No more CORS preflight
✅ No DNS lookup for subdomain
✅ No cross-origin cookie issues
✅ Faster + cleaner setup
✅ Cloudflare security rules still work
RewriteRule ^/dnxapi/(.*)$ http://localhost:8888/$1 [P,L]
Access-Control-Max-Age is URL-specific
Preflight result is cached per exact URL. Even a minor query change busts the cache.
Changing to same-origin (via proxy) is the real fix
Removes CORS entirely – NO MORE PREFLIGHT!
No DNS lookup.
No cross-site concerns.
Feels much faster.