The Problem That Cost Me 2 Hours
Picture this: Your frontend is calling a public API endpoint. No authentication barriers, no token requirements—just a simple, open endpoint. Everything should work smoothly, right?
Wrong.
I spent over 2 hours pulling my hair out because my cookies weren't being sent with the requests. The API was public, my code looked fine, but the browser simply refused to include the cookies I needed.
After diving deep into browser behavior, CORS policies, and cookie attributes, I finally understood what was happening. Here's everything I learned so you don't have to waste your afternoon debugging the same issue.
The Big Misconception: Public ≠ Cookie Freedom
Here's the crucial insight that took me way too long to understand:
Just because an endpoint is public doesn't mean the browser will freely send cookies to it.
A "public API" simply means there's no authentication barrier blocking access. But whether cookies are included in requests depends on an entirely different set of rules around:
Cookie attributes
Cross-origin request policies
CORS configuration
The browser doesn't care if the endpoint is "public" or "private"—it cares about security, and it enforces strict rules to protect users from attacks like CSRF (Cross-Site Request Forgery).
The Three Factors That Control Cookie Transmission
After hours of debugging and reading documentation, I identified three critical factors that determine whether cookies are sent with cross-origin requests:
1. SameSite Attribute: The Gatekeeper
The SameSite
attribute is the first line of defense. It tells the browser when a cookie should be included:
SameSite=Strict
Cookie is ONLY sent on same-site requests
Cross-site requests? Forget about it—no cookies for you
Most secure, but breaks legitimate cross-site use cases
SameSite=Lax
(default in modern browsers)
Cookie sent on same-site requests
Also sent on top-level navigations (like clicking a link)
NOT sent on cross-site API calls (like fetch or axios requests)
SameSite=None; Secure
Allows cookies on cross-site requests
MUST be paired with the Secure flag
Required for cookies to work in cross-origin API scenarios
Key takeaway: If your backend sets cookies without SameSite=None; Secure, they won't be sent in cross-origin API calls, period.
2. Client-Side Configuration: Ask for Credentials
Even with the right cookie attributes, you need to explicitly tell the browser to include cookies in cross-origin requests.
With Fetch API:
javascript
fetch(
{your_endpoint}, {
credentials: 'include' // This is the magic line
})
With Axios:
javascript
axios.get(
{your_endpoint}, {
withCredentials: true // Enable cookie transmission
})
Without these flags, the browser assumes you don't want cookies sent cross-origin, and it won't include them—even if everything else is configured correctly.
3. Server-Side CORS Headers: The Final Permission
Here's where my 2-hour bug was hiding: the server must explicitly allow credentialed requests with proper CORS headers.
Required headers:
`Access-Control-Allow-Credentials: true
Critical rules:
You CANNOT use Access-Control-Allow-Origin: * with credentials
The origin must be specific
Both headers must be present for cookies to be sent
In my case, the frontend had withCredentials: true, but the backend team hadn't configured these CORS headers. The browser saw the mismatch and silently blocked the cookies.
Other Cookie Flags Worth Understanding
While debugging, I also learned about two other important cookie attributes:
HttpOnly
Prevents JavaScript from accessing the cookie via document.cookie
Does NOT prevent the browser from sending it in HTTP requests
Great for security (prevents XSS attacks from stealing session cookies)
Secure
Cookie will ONLY be sent over HTTPS connections
Essential for production environments
Required when using SameSite=None
My Debugging Story: What Went Wrong
Here's exactly what happened in my case:
Frontend setup: I was using axios with withCredentials: true ✅
Cookie attributes: Backend was setting cookies, but without proper SameSite attributes ❌
CORS headers: The public API team hadn't configured Access-Control-Allow-Credentials or a specific Access-Control-Allow-Origin ❌
The result? The browser looked at this mismatched configuration and said, "Nope, not sending those cookies."
Once the backend team added:
SameSite=None; Secure to the cookies
Access-Control-Allow-Credentials: true
Everything worked perfectly.
The Complete Checklist for Cross-Origin Cookies
Before you waste hours debugging like I did, check these three things:
✅ Backend Cookie Configuration
Cookies set with SameSite=None; Secure
Secure flag present (requires HTTPS)
HttpOnly flag if you don't need JS access
✅ Frontend Request Configuration
withCredentials: true (Axios) or credentials: 'include' (Fetch)
Requests are going to the correct domain
✅ Server CORS Headers
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin set to a specific origin (NOT *)
Headers present on both preflight and actual responses
All three must align perfectly, or cookies won't be sent.
Pro Debugging Tips
Chrome DevTools is your friend:
Open DevTools → Network tab
Click on the failed request
Go to the "Cookies" tab
Look for blocked cookies and the reason why
The browser will tell you exactly why cookies were blocked—you just need to know where to look!
The Bigger Picture: Why Browsers Are So Strict
You might wonder: "Why make this so complicated? Why not just send cookies everywhere?"
The answer is security. These restrictions exist to protect users from:
CSRF attacks: Malicious sites make requests on your behalf to legitimate sites where you're logged in
Data leakage: Third-party sites accessing your cookies and session data
Privacy violations: Tracking users across different websites
The browser assumes cookies contain sensitive data (like session tokens), so it requires explicit permission from both the client AND server before allowing cross-origin transmission.
Yes, it makes development harder. But it also keeps users safe.
Key Takeaways
After spending 2 hours on this bug, here's what I want you to remember:
"Public endpoint" doesn't mean "free cookie access"—browsers enforce strict security rules regardless of API accessibility
Three things must align:
Cookie attributes (SameSite=None; Secure)
Client configuration (withCredentials: true)
Server CORS headers (Allow-Credentials + specific Allow-Origin)
Silent failures are the worst—browsers often block cookies without obvious error messages, so know how to use DevTools to debug
This is a backend AND frontend problem—you can't fix it from just one side
Security over convenience—these restrictions exist for good reasons, even if they complicate development
Final Thoughts
Cross-origin cookie handling is one of those topics that seems simple until you actually need to implement it. The combination of cookie attributes, CORS policies, and browser security rules creates a complex system that's easy to misconfigure.
But once you understand the rules, debugging becomes much easier. Instead of randomly trying different configurations, you can systematically check each requirement and identify exactly what's missing.
I hope this post saves you from the 2 hours of frustration I went through. If you're working with public APIs and need to send cookies cross-origin, bookmark this checklist—you'll thank yourself later!
Top comments (0)