SameSite Attribute: Visual Guide

The SameSite attribute controls when cookies are sent with cross-site requests. Understanding this behavior is crucial for web security and proper cookie management.

Overview

The SameSite attribute has three possible values:

Same-Site Requests

When a request is made to the same domain where the cookie was set, it's considered a same-site request. In this case, cookies are sent regardless of the SameSite attribute value.

Same-Site Request Flow

flowchart TD A["site-a.com displayed in browser's address bar"] --> B["Send request with any method to site-a.com (AJAX or regular)"] B --> C{"Cookie promo_shown=1 is sent?"} C -->|Yes| D["promo_shown=1 is sent"] style C fill:#d1ffd1,stroke:#0b0,stroke-width:2px style D fill:#d1ffd1,stroke:#0b0,stroke-width:2px

Explanation:

Cross-Site Requests

When a request is made to a different domain than where the cookie was set, it's considered a cross-site request. The behavior depends on the SameSite attribute value.

Cross-Site Request Flow (General)

flowchart TD A["site-b.com displayed in browser's address bar"] --> B["Send request with any method to site-a.com (AJAX or regular)"] B --> C{"Cross-site request?"} C -->|Yes| D["promo_shown=1 is NOT sent (unless SameSite=None or specific conditions met)"] style C fill:#fff3cd,stroke:#ffc107,stroke-width:2px style D fill:#ffd1d1,stroke:#b00,stroke-width:2px

Explanation:

SameSite=Lax Behavior

SameSite=Lax is the default value in modern browsers. It provides a balance between security and usability.

Case 1: Safe Cross-Site GET with Top-Level Navigation

flowchart TD subgraph Lax_1 [Case 1: Safe cross-site GET with top-level navigation] A1["site-b.com in browser's address bar"] --> B1["Send GET request to site-a.com by clicking a link (URL changes)"] B1 --> C1["promo_shown=1 is sent"] end style C1 fill:#d1ffd1,stroke:#0b0,stroke-width:2px

Explanation:

Example:

<!-- User on site-b.com -->
<a href="https://site-a.com/products">Visit Shop</a>
<!-- User clicks link -->
<!-- Cookie IS sent to site-a.com -->

Case 2: Safe Cross-Site GET without Navigation

flowchart TD subgraph Lax_2 [Case 2: Safe cross-site GET without navigation] A2[site-b.com in browser's address bar] --> B2[Send GET request to site-a.com via AJAX, iframe, img, etc.] B2 --> C2[promo_shown=1 is NOT sent] end style C2 fill:#ffd1d1,stroke:#b00,stroke-width:2px

Explanation:

Examples:

<!-- User on site-b.com -->
<img src="https://site-a.com/pixel.gif">
<iframe src="https://site-a.com/widget"></iframe>
<script>
  fetch('https://site-a.com/api/data');
</script>
<!-- Cookie NOT sent in any of these cases -->

Case 3: Unsafe Methods

flowchart TD subgraph Lax_3 [Case 3: Unsafe methods] A3[site-b.com in browser's address bar] --> B3[Send POST, PATCH, PUT, DELETE request to site-a.com] B3 --> C3[promo_shown=1 is NOT sent] end style C3 fill:#ffd1d1,stroke:#b00,stroke-width:2px

Explanation:

Examples:

<!-- User on site-b.com -->
<form method="POST" action="https://site-a.com/login">
  <input name="username" value="user">
  <button type="submit">Login</button>
</form>
<!-- Cookie NOT sent -->
// User on site-b.com
fetch('https://site-a.com/api/order', {
  method: 'POST',
  body: JSON.stringify({ items: [...] })
});
// Cookie NOT sent

Unified SameSite Decision Tree

This comprehensive diagram shows how all SameSite values behave across different scenarios:

flowchart TD Start[Request Made] --> CheckSite{Same-site request?} CheckSite -->|Yes| SameSite[Cookie Always Sent] CheckSite -->|No| CheckSameSite{SameSite Attribute?} CheckSameSite -->|SameSite=None| CheckSecure{Secure attribute?} CheckSameSite -->|SameSite=Lax| CheckTopLevel{Top-level navigation?} CheckSameSite -->|SameSite=Strict| BlockStrict[❌ Cookie NOT Sent] CheckSecure -->|Yes| SendNone[✅ Cookie Sent] CheckSecure -->|No| BlockNoneSecure[❌ Cookie NOT Sent - Secure required] CheckTopLevel -->|Yes| CheckMethod{HTTP Method?} CheckTopLevel -->|No| BlockLaxEmbedded[❌ Cookie NOT Sent] CheckMethod -->|GET| SendLax[✅ Cookie Sent] CheckMethod -->|POST/PUT/PATCH/DELETE| BlockLaxUnsafe[❌ Cookie NOT Sent] style SameSite fill:#d1ffd1,stroke:#0b0,stroke-width:2px style SendNone fill:#d1ffd1,stroke:#0b0,stroke-width:2px style SendLax fill:#d1ffd1,stroke:#0b0,stroke-width:2px style BlockStrict fill:#ffd1d1,stroke:#b00,stroke-width:2px style BlockNoneSecure fill:#ffd1d1,stroke:#b00,stroke-width:2px style BlockLaxEmbedded fill:#ffd1d1,stroke:#b00,stroke-width:2px style BlockLaxUnsafe fill:#ffd1d1,stroke:#b00,stroke-width:2px

Explanation:

Comparison Table

Scenario Same-Site? Top-Level? Method SameSite=None SameSite=Lax SameSite=Strict
User on site-a.com navigates to site-a.com/products ✅ Yes N/A Any ✅ Sent ✅ Sent ✅ Sent
User on site-b.com clicks link to site-a.com ❌ No ✅ Yes GET ✅ Sent ✅ Sent ❌ Not sent
User on site-b.com has iframe loading site-a.com ❌ No ❌ No GET ✅ Sent ❌ Not sent ❌ Not sent
User on site-b.com POSTs form to site-a.com ❌ No ✅ Yes POST ✅ Sent ❌ Not sent ❌ Not sent
User on site-b.com makes AJAX GET to site-a.com ❌ No ❌ No GET ✅ Sent ❌ Not sent ❌ Not sent

Legend:

Detailed Scenarios

Scenario 1: Same-Site Request

Setup:

Flow:

flowchart LR A[User on site-a.com] --> B[Request to site-a.com/products] B --> C[✅ Cookie Sent] style C fill:#d1ffd1,stroke:#0b0,stroke-width:2px

Result: Cookie IS sent (same-site request)

Scenario 2: Cross-Site GET Navigation (Lax)

Setup:

Flow:

flowchart LR A[User on site-b.com] --> B[Clicks link to site-a.com] B --> C[Top-level GET navigation] C --> D[✅ Cookie Sent - SameSite=Lax] style D fill:#d1ffd1,stroke:#0b0,stroke-width:2px

Result: Cookie IS sent because:

Scenario 3: Cross-Site Embedded Resource (Lax)

Setup:

Flow:

flowchart LR A[User on site-b.com] --> B[Loads iframe from site-a.com] B --> C[Not top-level navigation] C --> D[❌ Cookie NOT Sent - SameSite=Lax] style D fill:#ffd1d1,stroke:#b00,stroke-width:2px

Result: Cookie NOT sent (embedded resource, not top-level navigation)

Scenario 4: Cross-Site POST (Lax)

Setup:

Flow:

flowchart LR A[User on site-b.com] --> B[POSTs form to site-a.com] B --> C[Unsafe method POST] C --> D[❌ Cookie NOT Sent - SameSite=Lax] style D fill:#ffd1d1,stroke:#b00,stroke-width:2px

Result: Cookie NOT sent (cross-site POST, unsafe method)

Scenario 5: Cross-Site Request (Strict)

Setup:

Flow:

flowchart LR A[User on site-b.com] --> B[Request to site-a.com] B --> C[Cross-site request] C --> D[❌ Cookie NOT Sent - SameSite=Strict] style D fill:#ffd1d1,stroke:#b00,stroke-width:2px

Result: Cookie NOT sent (cross-site request blocked by Strict)

Scenario 6: Cross-Site Request (None)

Setup:

Flow:

flowchart LR A[User on site-b.com] --> B[Request to site-a.com] B --> C[Cross-site request] C --> D[✅ Cookie Sent - SameSite=None] style D fill:#d1ffd1,stroke:#0b0,stroke-width:2px

Result: Cookie IS sent (SameSite=None allows all cross-site requests)

Security Implications

SameSite=None

Security Level: Lowest

SameSite=Lax

Security Level: Moderate (Default)

SameSite=Strict

Security Level: Highest

Best Practices

When to Use SameSite=None

Example:

Set-Cookie: widget_session=abc; Domain=widget.example.com; SameSite=None; Secure

When to Use SameSite=Lax

Example:

Set-Cookie: session=abc123; Domain=example.com; SameSite=Lax; Secure; HttpOnly

When to Use SameSite=Strict

Example:

Set-Cookie: auth_token=xyz; Domain=bank.example.com; SameSite=Strict; Secure; HttpOnly

Summary

Key Points

  1. Same-site requests: Cookies are always sent regardless of SameSite value
  2. SameSite=None: Cookies sent with all cross-site requests (requires Secure)
  3. SameSite=Lax: Cookies sent with cross-site top-level GET navigation only
  4. SameSite=Strict: Cookies sent with same-site requests only

Quick Reference

SameSite Value Same-Site Cross-Site GET Navigation Cross-Site POST Cross-Site Embedded
None ✅ Sent ✅ Sent ✅ Sent ✅ Sent
Lax ✅ Sent ✅ Sent ❌ Not sent ❌ Not sent
Strict ✅ Sent ❌ Not sent ❌ Not sent ❌ Not sent

Decision Guide

Use SameSite=None if:

Use SameSite=Lax if:

Use SameSite=Strict if: