Table of Contents
Introduction
In Part 1, we covered CORS basics, and in Part 2, we explored Fetch API modes. Now, let's dive into the more advanced aspects of CORS: handling different HTTP methods, custom headers, and managing credentials.
CORS Headers Deep Dive
When working with complex cross-origin requests, understanding how different CORS headers interact becomes crucial. Let's explore each important header in detail.
Access-Control-Allow-Methods
This header is crucial when your API supports multiple HTTP methods. Here's how it works:
app.options("/products", cors({ methods: ['DELETE'], origin: function (origin, callback) { if(allowedOrigins.indexOf(origin) !== -1){ callback(null, origin) } else { callback(null, null); } } }))
If we try a PUT request without the proper method configuration:
After adding the correct method:
// Server code to allow DELETE and PUT method in preflight request app.options("/products", cors({ methods: ['DELETE', 'PUT'],
Access-Control-Allow-Headers
When your requests need custom headers, you need to explicitly allow them:
app.options("/products", cors({ methods: ['DELETE', 'PUT'], allowedHeaders: ['X-Token-Age'], origin: function (origin, callback) { if(allowedOrigins.indexOf(origin) !== -1){ callback(null, origin) } else { callback(null, null); } } }))
Let's try sending multiple custom headers:
fetch(`http://localhost:3004/products`, { method: "PUT", mode: "cors", headers: { "X-Token-Age": "3600", "X-Token-Length": "1800" } })
Without proper configuration, we get an error:
But with proper configuration it will work properly
app.options("/products", cors({ methods: ['DELETE', 'PUT'], allowedHeaders: ['X-Token-Age', 'X-Token-Length'], ...
Working with Credentials
Understanding Credentials in CORS
The Access-Control-Allow-Credentials header is essential when your application needs to send cookies or authentication headers in cross-origin requests.
fetch(`http://localhost:3004/products`, { method: "PUT", mode: "cors", headers: { "X-Token-Age": "3600"}, credentials: "include" // to send cookies from browser })
Without proper credentials configuration in server, you'll see this error:
To fix this, configure your server:
app.options("/products", cors({ methods: ['DELETE', 'PUT'], allowedHeaders: ['X-Token-Age'], credentials: true, //----->>>required origin: function (origin, callback) { if(allowedOrigins.indexOf(origin) !== -1){ callback(null, origin) } else { callback(null, null); } } }))
Cookie Behavior in Cross-Origin Requests
When working with cookies across domains, there are important considerations:
-
Domain Limitations
// This won't work from example.com document.cookie = "token=wqerbkljasdfhy;domain=stackoverflow.com;" -
Subdomain Sharing
- Cookies can be shared between subdomains of the same root domain
- Set the domain attribute to your root domain
- Example:
.example.comallows sharing betweenapi.example.comandwww.example.com
Cookie Security Attributes
Important cookie attributes for secure applications:
// Set secure cookie res.cookie('sessionId', 'abc123', { domain: '.example.com', secure: true, httpOnly: true, sameSite: 'Strict' });
-
Secure Attribute
- Ensures cookies are only sent over HTTPS
- Essential for production environments
-
HttpOnly Attribute
- Prevents JavaScript access to cookies
- Protects against XSS attacks
-
SameSite Attribute
- Controls how cookies behave in cross-site requests
- Options: Strict, Lax, None
Best Practices
-
Method Handling
app.use(cors({ methods: ['GET', 'POST', 'PUT', 'DELETE'], allowedHeaders: ['Content-Type', 'Authorization'], credentials: true, origin: allowedOrigins })); -
Security Headers
- Always use specific origins instead of wildcards
- Implement proper preflight handling
- Keep allowed headers list minimal
-
Cookie Security
// Production cookie settings app.use(session({ cookie: { secure: true, httpOnly: true, domain: '.example.com', sameSite: 'Strict' } }));