[{"data":1,"prerenderedAt":376},["ShallowReactive",2],{"blog-best-practices/cors":3},{"id":4,"title":5,"body":6,"category":350,"date":351,"dateModified":351,"description":352,"draft":353,"extension":354,"faq":355,"featured":353,"headerVariant":361,"image":362,"keywords":362,"meta":363,"navigation":364,"ogDescription":365,"ogTitle":362,"path":366,"readTime":367,"schemaOrg":368,"schemaType":369,"seo":370,"sitemap":371,"stem":372,"tags":373,"twitterCard":374,"__hash__":375},"blog/blog/best-practices/cors.md","CORS Best Practices: Configuration, Security, and Common Mistakes",{"type":7,"value":8,"toc":334},"minimark",[9,16,25,30,33,43,47,50,65,69,72,81,85,88,97,101,104,113,117,120,129,133,136,145,149,227,246,274,278,281,303,322],[10,11,12],"tldr",{},[13,14,15],"p",{},"The #1 CORS security best practice is to whitelist specific origins rather than using wildcards. Never use Access-Control-Allow-Origin: * with credentials. Be careful with dynamic origin reflection, and understand that CORS protects users, not your API. Proper CORS configuration prevents cross-origin data theft.",[17,18,19],"quotable-box",{},[20,21,22],"blockquote",{},[13,23,24],{},"\"CORS protects users, not servers. A misconfigured policy lets attackers weaponize your users' browsers against your own API.\"",[26,27,29],"h2",{"id":28},"what-cors-actually-protects","What CORS Actually Protects",[13,31,32],{},"CORS (Cross-Origin Resource Sharing) is a browser security feature that prevents malicious websites from accessing data from other sites.",[34,35,36],"warning-box",{},[13,37,38,42],{},[39,40,41],"strong",{},"CORS protects users, not your API."," CORS is enforced by browsers. Attackers can still call your API directly with tools like curl. CORS prevents a malicious site from using a victim's browser to steal their data from your API.",[26,44,46],{"id":45},"best-practice-1-whitelist-specific-origins-3-min","Best Practice 1: Whitelist Specific Origins 3 min",[13,48,49],{},"Never use wildcards (*) for APIs that handle sensitive data:",[51,52,54],"code-block",{"label":53},"Express CORS configuration",[55,56,61],"pre",{"className":57,"code":59,"language":60},[58],"language-text","import cors from 'cors';\n\n// WRONG: Allows any origin\napp.use(cors({ origin: '*' }));\n\n// WRONG: Allows any origin with credentials (browser blocks this anyway)\napp.use(cors({ origin: '*', credentials: true }));\n\n// CORRECT: Whitelist specific origins\nconst allowedOrigins = [\n  'https://yourdomain.com',\n  'https://app.yourdomain.com',\n  process.env.NODE_ENV === 'development' && 'http://localhost:3000',\n].filter(Boolean);\n\napp.use(cors({\n  origin: (origin, callback) => {\n    // Allow requests with no origin (mobile apps, curl, etc.)\n    if (!origin) return callback(null, true);\n\n    if (allowedOrigins.includes(origin)) {\n      callback(null, true);\n    } else {\n      callback(new Error('Not allowed by CORS'));\n    }\n  },\n  credentials: true,\n}));\n","text",[62,63,59],"code",{"__ignoreMap":64},"",[26,66,68],{"id":67},"best-practice-2-avoid-dynamic-origin-reflection-2-min","Best Practice 2: Avoid Dynamic Origin Reflection 2 min",[13,70,71],{},"Reflecting the Origin header back is dangerous:",[51,73,75],{"label":74},"Dangerous origin reflection",[55,76,79],{"className":77,"code":78,"language":60},[58],"// DANGEROUS: Reflects any origin\napp.use(cors({\n  origin: (origin, callback) => {\n    callback(null, origin); // Reflects attacker's origin!\n  },\n  credentials: true,\n}));\n\n// This allows ANY site to access your API with cookies\n// Attacker's site can steal user data\n\n// CORRECT: Validate against whitelist\napp.use(cors({\n  origin: (origin, callback) => {\n    if (!origin || allowedOrigins.includes(origin)) {\n      callback(null, origin);\n    } else {\n      callback(new Error('Not allowed'));\n    }\n  },\n  credentials: true,\n}));\n",[62,80,78],{"__ignoreMap":64},[26,82,84],{"id":83},"best-practice-3-configure-methods-and-headers-2-min","Best Practice 3: Configure Methods and Headers 2 min",[13,86,87],{},"Only allow the HTTP methods and headers you actually use:",[51,89,91],{"label":90},"Restrictive CORS configuration",[55,92,95],{"className":93,"code":94,"language":60},[58],"app.use(cors({\n  origin: allowedOrigins,\n  credentials: true,\n  methods: ['GET', 'POST', 'PUT', 'DELETE'], // Only methods you use\n  allowedHeaders: ['Content-Type', 'Authorization'], // Only headers you need\n  exposedHeaders: ['X-Request-Id'], // Headers client can read\n  maxAge: 86400, // Cache preflight for 24 hours\n}));\n",[62,96,94],{"__ignoreMap":64},[26,98,100],{"id":99},"best-practice-4-handle-preflight-requests-3-min","Best Practice 4: Handle Preflight Requests 3 min",[13,102,103],{},"Browsers send OPTIONS requests before certain cross-origin requests:",[51,105,107],{"label":106},"Manual preflight handling",[55,108,111],{"className":109,"code":110,"language":60},[58],"// When not using cors middleware\napp.options('*', (req, res) => {\n  const origin = req.headers.origin;\n\n  if (allowedOrigins.includes(origin)) {\n    res.setHeader('Access-Control-Allow-Origin', origin);\n    res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');\n    res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');\n    res.setHeader('Access-Control-Allow-Credentials', 'true');\n    res.setHeader('Access-Control-Max-Age', '86400');\n    res.status(204).end();\n  } else {\n    res.status(403).end();\n  }\n});\n",[62,112,110],{"__ignoreMap":64},[26,114,116],{"id":115},"best-practice-5-cors-with-cookies-3-min","Best Practice 5: CORS with Cookies 3 min",[13,118,119],{},"Cookies require specific CORS configuration:",[51,121,123],{"label":122},"CORS with credentials",[55,124,127],{"className":125,"code":126,"language":60},[58],"// Server: Allow credentials\napp.use(cors({\n  origin: 'https://app.yourdomain.com', // Must be specific, not *\n  credentials: true, // Allow cookies\n}));\n\n// Client: Include credentials in fetch\nfetch('https://api.yourdomain.com/user', {\n  credentials: 'include', // Send cookies\n});\n\n// Cookie must also have proper settings\nres.cookie('session', token, {\n  httpOnly: true,\n  secure: true,\n  sameSite: 'none', // Required for cross-origin\n  domain: '.yourdomain.com', // Shared domain\n});\n",[62,128,126],{"__ignoreMap":64},[26,130,132],{"id":131},"best-practice-6-per-route-cors-2-min","Best Practice 6: Per-Route CORS 2 min",[13,134,135],{},"Different routes may need different CORS policies:",[51,137,139],{"label":138},"Route-specific CORS",[55,140,143],{"className":141,"code":142,"language":60},[58],"import cors from 'cors';\n\n// Public API: Allow any origin, no credentials\nconst publicCors = cors({\n  origin: '*',\n  methods: ['GET'],\n});\n\n// Private API: Strict origin, with credentials\nconst privateCors = cors({\n  origin: allowedOrigins,\n  credentials: true,\n  methods: ['GET', 'POST', 'PUT', 'DELETE'],\n});\n\n// Apply per route\napp.get('/api/public/status', publicCors, statusHandler);\napp.use('/api/user', privateCors, userRouter);\napp.use('/api/admin', privateCors, adminRouter);\n",[62,144,142],{"__ignoreMap":64},[26,146,148],{"id":147},"common-cors-mistakes","Common CORS Mistakes",[150,151,152,168],"table",{},[153,154,155],"thead",{},[156,157,158,162,165],"tr",{},[159,160,161],"th",{},"Mistake",[159,163,164],{},"Risk",[159,166,167],{},"Prevention",[169,170,171,183,194,205,216],"tbody",{},[156,172,173,177,180],{},[174,175,176],"td",{},"origin: \"*\" with credentials",[174,178,179],{},"Browser blocks (but intent is wrong)",[174,181,182],{},"Whitelist specific origins",[156,184,185,188,191],{},[174,186,187],{},"Reflecting any origin",[174,189,190],{},"Cross-origin data theft",[174,192,193],{},"Validate against whitelist",[156,195,196,199,202],{},[174,197,198],{},"Trusting null origin",[174,200,201],{},"Sandbox escape attacks",[174,203,204],{},"Never allow null origin",[156,206,207,210,213],{},[174,208,209],{},"Regex subdomain matching",[174,211,212],{},"Bypass via evil.com.attacker.com",[174,214,215],{},"Use exact matches",[156,217,218,221,224],{},[174,219,220],{},"CORS on public files",[174,222,223],{},"Unnecessary (use CDN)",[174,225,226],{},"CORS for API only",[228,229,230,235],"info-box",{},[231,232,234],"h3",{"id":233},"external-resources","External Resources",[13,236,237,238,245],{},"For comprehensive CORS documentation and specifications, see the ",[239,240,244],"a",{"href":241,"rel":242},"https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS",[243],"nofollow","MDN CORS documentation",", which covers browser behavior, preflight requests, and all CORS headers in detail.",[247,248,249,256,262,268],"faq-section",{},[250,251,253],"faq-item",{"question":252},"When should I use Access-Control-Allow-Origin: *?",[13,254,255],{},"Only for truly public resources that do not require authentication and contain no sensitive data. Public APIs (like weather data), static files, or public documentation. Never with credentials.",[250,257,259],{"question":258},"Why does CORS not protect my API from hackers?",[13,260,261],{},"CORS is enforced by browsers only. Attackers can bypass it using curl, Postman, or their own servers. CORS protects users by preventing malicious sites from using their browser to access your API.",[250,263,265],{"question":264},"How do I allow multiple origins?",[13,266,267],{},"CORS only allows one origin in the header. Use a function to check the request origin against your whitelist and return that origin if allowed. See the examples above.",[250,269,271],{"question":270},"What is the null origin and should I allow it?",[13,272,273],{},"The null origin comes from sandboxed iframes, file:// URLs, and some redirects. Allowing null origin can enable attacks from sandboxed pages. Generally, do not allow it unless you have a specific need.",[26,275,277],{"id":276},"further-reading","Further Reading",[13,279,280],{},"Put these practices into action with our step-by-step guides.",[282,283,284,291,297],"ul",{},[285,286,287],"li",{},[239,288,290],{"href":289},"/blog/how-to/add-security-headers","Add security headers to your app",[285,292,293],{},[239,294,296],{"href":295},"/blog/checklists/pre-deployment-security-checklist","Pre-deployment security checklist",[285,298,299],{},[239,300,302],{"href":301},"/blog/getting-started/first-scan","Run your first security scan",[304,305,306,312,317],"related-articles",{},[307,308],"related-card",{"description":309,"href":310,"title":311},"All security headers explained","/blog/best-practices/headers","Security Headers",[307,313],{"description":314,"href":315,"title":316},"Secure API design patterns","/blog/best-practices/api-design","API Security",[307,318],{"description":319,"href":320,"title":321},"Secure auth with CORS","/blog/best-practices/authentication","Authentication",[323,324,327,331],"cta-box",{"href":325,"label":326},"/","Start Free Scan",[26,328,330],{"id":329},"check-your-cors-configuration","Check Your CORS Configuration",[13,332,333],{},"Scan your API for CORS misconfigurations.",{"title":64,"searchDepth":335,"depth":335,"links":336},2,[337,338,339,340,341,342,343,344,348,349],{"id":28,"depth":335,"text":29},{"id":45,"depth":335,"text":46},{"id":67,"depth":335,"text":68},{"id":83,"depth":335,"text":84},{"id":99,"depth":335,"text":100},{"id":115,"depth":335,"text":116},{"id":131,"depth":335,"text":132},{"id":147,"depth":335,"text":148,"children":345},[346],{"id":233,"depth":347,"text":234},3,{"id":276,"depth":335,"text":277},{"id":329,"depth":335,"text":330},"best-practices","2026-01-21","CORS security best practices. Learn to configure Cross-Origin Resource Sharing correctly, avoid common mistakes, and protect your API from cross-origin attacks.",false,"md",[356,357,358,360],{"question":252,"answer":255},{"question":258,"answer":261},{"question":264,"answer":359},"CORS only allows one origin in the header. Use a function to check the request origin against your whitelist and return that origin if allowed.",{"question":270,"answer":273},"vibe-green",null,{},true,"Configure CORS correctly to protect your API from cross-origin attacks.","/blog/best-practices/cors","11 min read","[object Object]","Article",{"title":5,"description":352},{"loc":366},"blog/best-practices/cors",[],"summary_large_image","oKxc9MqiEs8m3pV1cjD3KJNgfQwrx32coOgwVH_hOjs",1775843926276]