SameSite=Lax: Understanding the Default Cookie Policy
SameSite=Lax is the default SameSite attribute value in modern browsers and represents a balanced approach to cookie security that protects against CSRF attacks while maintaining usability for normal navigation.
What is SameSite=Lax?
SameSite=Lax is a cookie attribute that controls when cookies are sent with cross-site requests. It provides a middle ground between security and usability:
- More permissive than Strict: Allows cookies to be sent with cross-site top-level navigations
- More restrictive than None: Blocks cookies from cross-site POST requests and embedded resources
- Default behavior: Modern browsers default to
SameSite=Laxif noSameSiteattribute is specified
Cookie Behavior with SameSite=Lax
Understanding when cookies with SameSite=Lax are sent requires analyzing three key factors:
- Same-site vs cross-site: Is the request going to the same domain where the cookie was set?
- Top-level navigation: Does the request change the URL bar (top-level) or load embedded content?
- HTTP method: Is the request using GET or POST?
Same-Site Requests (Always Sent)
Same-site requests occur when a request is made to the same domain where the cookie was set. Cookies with SameSite=Lax are always sent with same-site requests, regardless of HTTP method or request type.
Let's say a user is on site-a.com and navigates to another page on site-a.com (e.g., clicks a link to site-a.com/products). This is a same-site request. Both Lax and Strict cookies are sent to site-a.com because the request is going to the same domain where the cookies were set.
Examples of same-site requests:
- Direct navigation:
https://shop.example.com→https://shop.example.com/products - API calls:
fetch('https://shop.example.com/api/cart') - Form submissions:
<form action="https://shop.example.com/checkout" method="POST"> - AJAX requests:
fetch('https://shop.example.com/data', { method: 'POST' })
Result: Cookie is always sent with same-site requests.
Cross-Site Requests
Cross-site requests occur when a request is made to a different domain than where the cookie was set. The behavior depends on whether it's a top-level navigation and the HTTP method used.
Scenario 1: Cross-Site GET Navigation (Lax Cookies Sent)
Let's say a user is on site-a.com and clicks on a link to go to site-b.com. This is a cross-site request. This is a top-level navigation and is a GET request, so Lax cookies are sent to site-b.com. However, Strict cookies are not sent because it is, after all, a cross-site request.
Key characteristics:
- Cross-site request (different domain)
- Top-level navigation (URL bar changes)
- GET method
- Result: Lax cookies ✅ sent, Strict cookies ❌ not sent
Other examples of cross-site GET navigation:
- User follows a redirect (301/302) to a different domain
- User submits a GET form that navigates to a different domain
- User types a URL in the address bar and navigates to a different domain
- User clicks browser back/forward buttons that navigate to a different domain
Scenario 2: Cross-Site Embedded Resources (Lax Cookies NOT Sent)
The user is on site-a.com and there is an iframe in which site-b.com is loaded. This is a cross-site request, but it's not a top-level navigation (the user is still on site-a.com, i.e. the URL bar doesn't change when the iframe is loaded). Therefore neither Lax nor Strict cookies are sent to site-b.com.
Key characteristics:
- Cross-site request (different domain)
- Not top-level navigation (URL bar stays the same)
- Result: Lax cookies ❌ not sent, Strict cookies ❌ not sent
Other examples of cross-site embedded resources:
- Image tags:
<img src="https://site-b.com/pixel.gif"> - AJAX/Fetch requests:
fetch('https://site-b.com/api/data') - CSS/JavaScript resources:
<link href="https://site-b.com/style.css"> - Background tracking pixels:
<img src="https://tracker.com/pixel">
Scenario 3: Cross-Site POST Requests (Lax Cookies NOT Sent)
The user is on site-a.com which POSTs a form to site-b.com. This is a cross-site request, but the method (POST) is unsafe. It doesn't meet the criteria for Lax cookies going cross-site, so neither Lax nor Strict cookies are sent to site-b.com.
Key characteristics:
- Cross-site request (different domain)
- POST method (unsafe method)
- Even if it's a top-level navigation, POST doesn't qualify for Lax
- Result: Lax cookies ❌ not sent, Strict cookies ❌ not sent
Why this protects against CSRF:
- Prevents malicious sites from making authenticated POST requests
- Forces attackers to use GET (which shouldn't have side effects) or SameSite=None (which requires Secure)
Other examples of cross-site POST requests:
- POST form submission:
<form method="POST" action="https://site-b.com/login"> - AJAX POST:
fetch('https://site-b.com/api/order', { method: 'POST' })
Summary: Cookie Behavior Comparison
| Scenario | Same-Site? | Top-Level? | Method | SameSite=Lax | SameSite=Strict |
|---|---|---|---|---|---|
User on site-a.com navigates to site-a.com/products |
✅ Yes | N/A | Any | ✅ Sent | ✅ Sent |
User on site-a.com clicks link to site-b.com |
❌ No | ✅ Yes | GET | ✅ Sent | ❌ Not sent |
User on site-a.com has iframe loading site-b.com |
❌ No | ❌ No | GET | ❌ Not sent | ❌ Not sent |
User on site-a.com POSTs form to site-b.com |
❌ No | ✅ Yes | POST | ❌ Not sent | ❌ Not sent |
Key Takeaways:
- Same-site requests: Both Lax and Strict cookies are sent
- Cross-site GET navigation: Only Lax cookies are sent
- Cross-site embedded resources: Neither Lax nor Strict cookies are sent
- Cross-site POST: Neither Lax nor Strict cookies are sent (CSRF protection)
Understanding Top-Level Navigation
Key Concept
Top-level navigation is the critical distinction for SameSite=Lax. It refers to navigation that changes the browser's main window/tab URL to a different domain.
Visual Indicators
Top-Level Navigation:
- URL bar changes
- Browser history updates
- Page content completely replaces
- User sees new domain in address bar
Non-Top-Level (Embedded):
- URL bar stays the same
- Content loads within existing page
- Resource loads in background
- User doesn't see new domain in address bar
Decision Flow
Security Implications
CSRF Protection
SameSite=Lax provides protection against Cross-Site Request Forgery (CSRF) attacks:
How CSRF Protection Works:
- Attackers cannot use cross-site POST requests to trigger actions
- Cookies are blocked from cross-site POST forms
- Prevents unauthorized actions from malicious sites
Example Attack Prevented:
<!-- Malicious site: attacker.com -->
<form method="POST" action="https://bank.com/transfer">
<input name="to" value="attacker-account">
<input name="amount" value="1000">
</form>
<script>document.forms[0].submit();</script>
With SameSite=Lax:
- Cookie NOT sent with POST request
- Attack fails (user not authenticated)
Without SameSite=Lax (or with SameSite=None):
- Cookie IS sent with POST request
- Attack succeeds (user authenticated)
What's NOT Protected
SameSite=Lax does NOT protect against:
Cross-Site GET Requests: Cookies are sent with GET navigations
- Can be exploited if GET endpoints perform side effects
- Best practice: Use POST for state-changing operations
Same-Site Attacks: Cookies are always sent with same-site requests
- Requires additional protection (CSRF tokens, etc.)
XSS Attacks: If site has XSS vulnerability, cookies can be stolen
- Use
HttpOnlyattribute to prevent JavaScript access
- Use
Browser Default Behavior
Modern Browsers (2020+)
Most modern browsers default to SameSite=Lax if no SameSite attribute is specified:
- Chrome: Defaults to
SameSite=Lax(since Chrome 80) - Firefox: Defaults to
SameSite=Lax(since Firefox 69) - Safari: Defaults to
SameSite=Lax(since Safari 13) - Edge: Defaults to
SameSite=Lax(since Edge 80)
Legacy Behavior
Older browsers or browsers with legacy settings may:
- Not enforce
SameSiteattribute - Treat cookies without
SameSiteasSameSite=None - Require explicit
SameSite=Laxsetting
Best Practice: Always explicitly set SameSite=Lax for clarity and compatibility.
Real-World Examples
Example 1: E-commerce Site
Scenario: User shops on shop.example.com, then clicks link from email to continue shopping.
Setup:
Set-Cookie: session=abc123; Domain=shop.example.com; SameSite=Lax; Secure
Flow:
- User receives email from
newsletter.example.com - Email contains link:
<a href="https://shop.example.com/checkout">Complete Purchase</a> - User clicks link
- Result: Cookie IS sent (cross-site GET navigation)
Why it works: User expects to stay logged in when clicking email links.
Example 2: Authentication Cookie
Scenario: User logs in to app.example.com, then clicks link from documentation site.
Setup:
Set-Cookie: auth_token=xyz789; Domain=app.example.com; SameSite=Lax; Secure; HttpOnly
Flow:
- User visits
docs.example.com - Documentation links to:
<a href="https://app.example.com/dashboard">Open App</a> - User clicks link
- Result: Cookie IS sent (cross-site GET navigation)
Why it works: User expects seamless navigation between related sites.
Example 3: CSRF Protection for Payment
Scenario: Payment form on checkout page.
Setup:
Set-Cookie: session=abc123; Domain=shop.example.com; SameSite=Lax; Secure
Flow:
- User on
shop.example.comcheckout page - Form:
<form method="POST" action="https://shop.example.com/payment"> - User submits payment
- Result: Cookie IS sent (same-site POST)
Malicious Attack Attempt:
- Attacker creates page:
<form method="POST" action="https://shop.example.com/payment"> - User visits attacker's page
- Attacker attempts to submit form
- Result: Cookie NOT sent (cross-site POST blocked)
Why it works: CSRF protection prevents unauthorized payment submissions.
Example 4: Analytics Tracking Pixel
Scenario: Third-party analytics pixel embedded on website.
Setup:
Set-Cookie: tracking_id=123; Domain=analytics.example.com; SameSite=Lax
Flow:
- User visits
shop.example.com - Page includes:
<img src="https://analytics.example.com/pixel?id=123"> - Pixel loads
- Result: Cookie NOT sent (cross-site embedded resource)
Why it fails: SameSite=Lax blocks cookies from embedded resources.
Solution: Use SameSite=None; Secure for third-party tracking cookies.
Comparison with Other SameSite Values
SameSite=Lax vs SameSite=Strict
| Scenario | SameSite=Lax | SameSite=Strict |
|---|---|---|
| Same-site requests | ✅ Sent | ✅ Sent |
| Cross-site GET navigation | ✅ Sent | ❌ Not sent |
| Cross-site POST | ❌ Not sent | ❌ Not sent |
| Cross-site embedded resources | ❌ Not sent | ❌ Not sent |
| User experience | More flexible | More restrictive |
| Security | CSRF protection | Maximum security |
When to use Strict:
- Maximum security requirements
- Sensitive operations (banking, healthcare)
- Can tolerate users needing to re-authenticate
When to use Lax:
- General web applications
- Want CSRF protection with good UX
- Need cookies for cross-site navigation
SameSite=Lax vs SameSite=None
| Scenario | SameSite=Lax | SameSite=None |
|---|---|---|
| Same-site requests | ✅ Sent | ✅ Sent |
| Cross-site GET navigation | ✅ Sent | ✅ Sent |
| Cross-site POST | ❌ Not sent | ✅ Sent |
| Cross-site embedded resources | ❌ Not sent | ✅ Sent |
| Security | CSRF protection | No CSRF protection |
| Requires Secure | No | Yes |
When to use None:
- Third-party widgets/embeds
- Cross-site tracking
- Cross-site POST operations needed
- Requires
Secureattribute (HTTPS only)
Common Pitfalls and Best Practices
Pitfall 1: Assuming POST Requests Work Cross-Site
Problem:
// This won't work cross-site
fetch('https://api.example.com/data', {
method: 'POST',
credentials: 'include' // Still won't send SameSite=Lax cookie
});
Solution: Use SameSite=None; Secure if you need cross-site POST cookies, or redesign to use same-site requests.
Pitfall 2: Expecting Embedded Resources to Send Cookies
Problem:
<!-- Cookie won't be sent -->
<iframe src="https://widget.example.com"></iframe>
Solution: Use SameSite=None; Secure for widget cookies, or use postMessage API for communication.
Pitfall 3: Not Setting SameSite Explicitly
Problem: Relying on browser defaults may cause inconsistent behavior.
Solution: Always explicitly set SameSite=Lax:
Set-Cookie: session=abc; SameSite=Lax; Secure
Best Practice 1: Use HttpOnly with SameSite=Lax
Recommended:
Set-Cookie: session=abc; SameSite=Lax; Secure; HttpOnly
Why: Prevents JavaScript access, reducing XSS attack surface.
Best Practice 2: Always Use Secure with SameSite=Lax
Recommended:
Set-Cookie: session=abc; SameSite=Lax; Secure
Why: Ensures cookies only sent over HTTPS, protecting against man-in-the-middle attacks.
Best Practice 3: Test Cross-Site Scenarios
Action: Test your application with:
- Links from external sites
- Embedded widgets
- Cross-site POST forms
- Redirect scenarios
Testing Scenarios
Test 1: Same-Site Navigation
- Visit
https://site-a.cookie-playground.pun7o.click - Set cookie:
test=value1; Domain=site-a.cookie-playground.pun7o.click; SameSite=Lax - Navigate to
https://site-a.cookie-playground.pun7o.click/index.html - Expected: Cookie IS sent
Test 2: Cross-Site GET Navigation
- Visit
https://site-b.cookie-playground.pun7o.click - Set cookie:
test=value2; Domain=site-a.cookie-playground.pun7o.click; SameSite=Lax - Click link from subdomain B to subdomain A
- Expected: Cookie IS sent (cross-site GET navigation)
Test 3: Cross-Site POST
- Visit
https://site-b.cookie-playground.pun7o.click - Set cookie:
test=value3; Domain=site-a.cookie-playground.pun7o.click; SameSite=Lax - Submit POST form from subdomain B to subdomain A
- Expected: Cookie NOT sent (cross-site POST blocked)
Test 4: Cross-Site Pixel Request
- Visit
https://site-b.cookie-playground.pun7o.click - Set cookie:
test=value4; Domain=site-a.cookie-playground.pun7o.click; SameSite=Lax - Load pixel:
<img src="https://site-a.cookie-playground.pun7o.click/pixel"> - Expected: Cookie NOT sent (embedded resource)
Test 5: Same-Site POST
- Visit
https://site-a.cookie-playground.pun7o.click - Set cookie:
test=value5; Domain=site-a.cookie-playground.pun7o.click; SameSite=Lax - Submit POST form on same domain
- Expected: Cookie IS sent (same-site request)
Summary
Key Points
- Default Behavior:
SameSite=Laxis the default in modern browsers - Same-Site: Cookies always sent with same-site requests
- Cross-Site GET: Cookies sent with top-level GET navigation
- Cross-Site POST: Cookies NOT sent (CSRF protection)
- Embedded Resources: Cookies NOT sent (privacy protection)
Use Cases
- ✅ Authentication cookies (balance security and UX)
- ✅ Session management (want CSRF protection)
- ✅ General web applications (most common use case)
- ❌ Third-party widgets (need
SameSite=None) - ❌ Cross-site POST operations (need
SameSite=None)
Security Benefits
- CSRF Protection: Blocks cross-site POST attacks
- Privacy: Prevents tracking via embedded resources
- Balance: Maintains usability for normal navigation
Related Topics
- Learn about Cookie Attributes for complete SameSite overview
- Understand Same-Site vs Cross-Site Requests for detailed request classification
- Review First-Party vs Third-Party cookies
- Explore Cross-Domain Behavior for subdomain scenarios