Set CORS Cookie In PHP (A Step-By-Step Guide)

Welcome to a tutorial on how to set a CORS cookie in PHP. Need to set a cookie in a cross-origin call? Annoyingly got rejected multiple times? Yep, that is security, and it took me hours to figure things out too. So here it is, a quick sharing of my working example – Read on!

ⓘ I have included a zip file with all the source code at the start of this tutorial, so you don’t have to copy-paste everything… Or if you just want to dive straight in.

 

 

TLDR – QUICK SLIDES

Fullscreen Mode – Click Here

 

TABLE OF CONTENTS

 

DOWNLOAD & NOTES

Firstly, here is the download link to the example code as promised.

 

QUICK NOTES

If you spot a bug, feel free to comment below. I try to answer short questions too, but it is one person versus the entire world… If you need answers urgently, please check out my list of websites to get help with programming.

 

EXAMPLE CODE DOWNLOAD

Click here to download all the example source code, I have released it under the MIT license, so feel free to build on top of it or use it in your own project.

 

 

TEST SERVER SETUP

For you guys who don’t have multiple domains to do testing, no sweat. Here is how I set up my localhost to emulate multiple domains.

 

OS HOSTS FILE

C:\Windows\System32\drivers\etc\hosts
127.0.0.1 site-a.com
127.0.0.1 site-b.com
::1 site-a.com
::1 site-b.com

First, open the hosts file and add the following entries. For the uninitiated, this is just a manual DNS override – Point site-a.com and site-b.com to localhost.

P.S. Mac/Linux users, yours should be in etc/hosts, but it may differ for different distros.

 

VIRTUAL HOSTS

httpd.conf
<VirtualHost *:80 *:443>
  DocumentRoot "D:/http/"
  ServerName localhost
</VirtualHost>
<VirtualHost *:80 *:443>
  DocumentRoot "D:/http/site-a/"
  ServerName site-a.com
</VirtualHost>
<VirtualHost *:80 *:443>
  DocumentRoot "D:/http/site-b/"
  ServerName site-b.com
</VirtualHost>

Next, create the project folders and map the virtual hosts accordingly. That is, https://site-a.com will point to D:/http/site-a/ and https://site-b.com will point to D:/http/site-b/.

P.S. This is for Apache. IIS and NGINX users – Do your own research.

 

 

SSL CERT

Lastly, using https:// is a vital part to make CORS cookies work. If you have not already done so, self-sign and set your own SSL certs. Not going to walk through that here, it is quite a hassle, but there are plenty of good guides all over the Internet – Do your own research again.

 

PHP CORS COOKIE

All right, let us now get into the step-by-step example of setting cookies in a CORS AJAX fetch call.

 

STEP 1) ACCESS SITE A & SITE B WITH HTTPS

  • Open your browser and access https://site-a.com/1-fetch.html. Yes, HTTPS.
  • Your browser or anti-virus is going to complain “self-signed SSL is unsafe”. Sure thing Sherlock, I signed that cert myself. Go ahead and click on “continue”.
  • Open a new tab and access https://site-b.com/3-show.html. Do the same and access the “unsafe site”.

 

STEP 2) FETCH CALL FROM SITE A

site-a/1-fetch.html
<!-- (A) DEMO BUTTON -->
<input type="button" value="CORS Cookie" onclick="demo()">
 
<script>
function demo () {
  // (B) CORS FETCH TO SITE B
  fetch("https://site-b.com/2-demo.php", {
    mode : "cors",
    credentials : "include"
  })
 
  // (C) FOR DEBUGGING... CONSOLE LOG ALL RESPONSES
  .then(res => {
    console.log(res);
    return res.text();
  })
  .then(res => console.log(res))
  .catch(err => console.log(err));
}
</script>

On site-a.com, we will do a fetch() call to site-b.com. Take note of the settings:

  • mode : "cors" Updated browsers should automatically detect and set this… But let’s just put this here for backward compatibility.
  • credentials : "include" That is, include the use of cookies.
  • Lastly, the use of https:// – Very important, using http:// will not allow cross-domain cookies to be set.

 

 

STEP 3) SITE B CALL HANDLER

site-b/2-demo.php
<?php
// (A) GET REQUEST ORIGIN
if (array_key_exists("HTTP_ORIGIN", $_SERVER)) {
  $origin = $_SERVER["HTTP_ORIGIN"];
} else if (array_key_exists("HTTP_REFERER", $_SERVER)) {
  $origin = $_SERVER["HTTP_REFERER"];
} else {
  $origin = $_SERVER["REMOTE_ADDR"];
}
 
// (B) CHECK ALLOWED DOMAINS
if (!in_array(
  parse_url($origin, PHP_URL_HOST),
  ["site-a.com", "site-b.com"]
)) {
  http_response_code(403);
  exit("$origin not allowed");
}
 
// (C) PROCEED
header("Access-Control-Allow-Origin: $origin");
header("Access-Control-Allow-Credentials: true");
setcookie("It", "Works", [
  "expires" => time()+3600,
  "path" => "/",
  "domain" => ".site-b.com",
  "secure" => true,
  "samesite" => "None"
]);
echo "OK";

On site-b.com:

  • Access-Control-Allow-Origin cannot be set as the wildcard *. Thus, the extra bits of code to restrict cross-domain calls from specific domains only.
  • Access-Control-Allow-Credentials must be set to true.
  • For the cookie itself:
    • Take note of the domain – We are making a call from site-a.com, but this cookie is meant for site-b.com.
    • The samesite rule is set to none, so secure must be set to true.

 

STEP 4) VERIFICATION

DEVELOPER’S CONSOLE

Go ahead and click on the test button. To verify the exchange in Chromium-based browsers, open the developer’s console > Network > 2-demo.php > Cookies.

 

SITE B ITSELF

site-b/3-show.html
<div id="demo"></div>
<script>
document.getElementById("demo").innerHTML = document.cookie;
</script>

Reload https://site-b.com/3-show.html, the cookie It=Works should also show up in the <div>.

 

 

EXTRA BITS & LINKS

That’s all for the tutorial, and here is a small section on some extras and links that may be useful to you.

 

HOW ABOUT XMLHTTPREQUEST?

Yes, it works too.

  • var xhr = new XMLHttpRequest();
  • xhr.withCredentials = true;

 

COMPATIBILITY CHECKS

Works on all modern “Grade A” browsers.

 

LINKS & REFERENCES

 

INFOGRAPHIC CHEAT SHEET

Set CORS Cookies In PHP (Click To Enlarge)

 

THE END

Thank you for reading, and we have come to the end. I hope that it has helped you to better understand, and if you want to share anything with this guide, please feel free to comment below. Good luck and happy coding!

Leave a Comment

Your email address will not be published. Required fields are marked *