Security Tip: Do You Know Your SameSite Cookies?
[Tip #133] SameSite=Lax is the Laravel default, and it quietly protects you from CSRF. So why do I keep finding SameSite=None in the apps I audit? Let's talk about what it does and how to use it safely.
As promised last time, it's time to look at SameSite cookies. Most developers will have heard them mentioned, and maybe even changed the value in Laravel, but how well do you really know about what they do and why?
Let's consider this cookie header:
laravel-session=eyJpdiI6Im...IjoiIn0%3D; expires=Mon, 03 Aug 2026 06:14:17 GMT; Max-Age=7200; path=/; secure; httponly; samesite=lax
Cookie headers follow a simple format, first is the actual cookie value (eyJpdiI6Im...IjoiIn0%3D), followed by a series of semi-colon (;) delimited keywords and key=value pairs. Hiding right at the end here is samesite=lax - which instructs the browser this cookie can only be included when it follows the lax rules.
lax (and strict and none), it is important to understand that "same-site" means anything on the same registrable domain, such as securinglaravel.com, subdomain.securinglaravel.com, www.securinglaravel.com. This means cookies sent to securinglaravel.com will also go to www.securinglaravel.com, etc.The exception to this are domains on the Public Suffix List, such as
github.io, and laravel.cloud, which shift the same-site boundary onto their subdomains (i.e. pottery.laravel.cloud vs valorin.laravel.cloud).There are three SameSite= options:
SameSite=Strict- Cookies are only ever sent on same-site requests, with no exceptions.
- This includes users clicking a link from a cross-site origin (i.e. clicking a link on
laravel.comthat leads tosecuringlaravel.com) - the cookies will not be included. - The result is that existing user sessions will not be resumed and a new session will be created.
- This is incredibly useful for sensitive cookies or high-security contexts, where you do not want a user landing on your site within an authenticated session from an external source.
SameSite=None; Secure- Cookies are always sent, no matter where the request comes from or how it is triggered.
- It requires the additional
Securekeyword, which tells the browser that cookie requires anhttpsconnection to be sent. The browser will rejectSameSite=Nonecookies withoutSecure. - Commonly used for embedded widgets, forms, frontends, and APIs where requests are being made cross-site.
- Because the browser sends these cookies automatically, any session they authenticate needs CSRF protection on state-changing requests. (As opposed to token-based API auth, where the credential isn't sent automatically and can't be forged.)
- If you need
SameSite=None, I recommend a separate set of cookies so your primary auth cookies remainLax. (Which leads us to...)
SameSite=Lax- Cookies are only sent during top-level navigation via a safe method, or put simply when clicking a link between cross-site domains that triggers a
GETrequest. - Cookies are not sent during non-
GETrequests (such as form submissions,fetch(), etc), or for embedded/loaded content (iframes, images, scripts). - This prevents an attacker from injecting payloads or triggering actions without the user's knowledge or permission, making it the sane default suitable for most apps.
- Default option in Laravel
config/session.php:'same_site' => env('SESSION_SAME_SITE', 'lax') - This is the default option in most browsers, but not all (Safari & Firefox 😕), so you should always define it to ensure it is correctly set.
- Cookies are only sent during top-level navigation via a safe method, or put simply when clicking a link between cross-site domains that triggers a
Isn't that just 500 words to tell me about something that is already default in Laravel?
Well, yes, but now you know about None and Strict 😈.
Well, yes, but I know from experience auditing many Laravel apps that SameSite=None is incredibly common, and it opens the door to CSRF attacks (as we saw here) in your apps. So my goal is to teach how SameSite works so if you need to reach for SameSite=None, you'll be warned and know how to do it safely.
Found this security tip useful? 👍
Subscribe now to get weekly Security Tips straight to your inbox, filled with practical, actionable advice to help you build safer apps.
Want to learn more? 🤓
Upgrade to a Premium Subscription for exclusive monthly In Depth articles! Your support directly funds my security work in the Laravel community. 🥰
Need a second set of eyes on your code?
Book in a Laravel Security Audit and Penetration Test today! I offer budget-friendly Security Reviews too.
Finally, connect with me on Twitter, Bluesky, phpc.social, and LinkedIn.