Cross-Domain LocalStorage

As you may know, LocalStorage is domain based. You can’t read or write from localstorage that’s on different domain, even if that’s subdomain. But there is iframe trick that you can use to store data from domain to it’s subdomain.

Basically to have Cross-Domain LocalStorage, you create an iframe that’s hosted on your other domain, then you send PostMessage to that iframe and inside iframe you set that value of localStorage.
Here is the code:

iFrame

window.onmessage = function(e) {
  if (e.origin !== "http://example.com") {
    return;
  }
  var payload = JSON.parse(e.data);
  localStorage.setItem(payload.key, JSON.stringify(payload.data));
};

main file

window.onload = function() {
    var win = document.getElementsByTagName('iframe')[0].contentWindow;
    var obj = {
       name: "Jack"
    };
    win.postMessage(JSON.stringify({key: 'storage', data: obj}), "*");
};

<a href="http://other.example.com/iframe.html">http://other.example.com/iframe.html</a>

If you want to save and load value from LocalStorage, you can PostMessage with method key that will indicate what action it need to take. You can also send message back from iframe to it’s parent with data read from localStorage.

iframe

document.domain = "example.com";
window.onmessage = function(e) {
    if (e.origin !== "http://example.com") {
        return;
    }
    var payload = JSON.parse(e.data);
    switch(payload.method) {
        case 'set':
            localStorage.setItem(payload.key, JSON.stringify(payload.data));
            break;
        case 'get':
            var parent = window.parent;
            var data = localStorage.getItem(payload.key);
            parent.postMessage(data, "*");
            break;
        case 'remove':
            localStorage.removeItem(payload.key);
            break;
    }
};

Your main page on example.com

window.onload = function() {
    var iframe = document.getElementsByTagName('iframe')[0];
    var win;
    // some browser (don't remember which one) throw exception when you try to access
    // contentWindow for the first time, it work when you do that second time
    try {
        win = iframe.contentWindow;
    } catch(e) {
        win = iframe.contentWindow;
    }
    var obj = {
       name: "Jack"
    };
    // save obj in subdomain localStorage
    win.postMessage(JSON.stringify({key: 'storage', method: "set", data: obj}), "*");
    window.onmessage = function(e) {
        if (e.origin != "http://other.example.com") {
            return;
        }
        // this will log "Jack"
        console.log(JSON.parse(e.data).name);
    };
    // load previously saved data
    win.postMessage(JSON.stringify({key: 'storage', method: "get"}), "*");
};

Here is a demo that use my sysend library to send messages to other tab in same browser (the library use localStorage for this). It use iframe proxy to send message from jcubic.pl to terminal.jcubic.pl and vice versa. Iframe is hosted on both domains. (everything is done by the library, you only need to include the file from repo on other.example.com domain and call sysend.proxy('http://other.example.com')).

The only caveat is that in Firefox, it seems that you need to enable CORS on Iframe. If you use apache server you can enable it in .htaccess file with this code:

Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Methods: "GET"

it will enable CORS for all files, if you want to enable CORS for a single file, this should work:

<Files "iframe.html">
  Header set Access-Control-Allow-Origin "*"
  Header set Access-Control-Allow-Methods: "GET"
</Files>

And that’s it you now can have Cross-Domain LocalStorage.

Published by

jcubic

I'm a web developer from Poland with a focus on JavaScript.

28 thoughts on “Cross-Domain LocalStorage”

  1. Hi Jakub: thanks for the tutorial… it’s not very clear how the iframes should be set up. You have two code snippets labeled “iframe”. Are you using two iframes or just one?

  2. This is by far the best and most useful tutorial I’ve seen regarding HTML5 local storage usage. Thank you so much for sharing this knowledge with us!

  3. This isn’t that clear to be honest. You need to identify which is you parent and which is your sub-domain

    1. You don’t you can host iframe on both domains, on domain A you include iframe from domain B and on domain B you include iframe from domain A. That way you can have two directional connection, you send data from A to B and from B to A.

      If you have one iframe you always send message from host page to domain inside iframe.

  4. I’m setting localstorage from a webpage, and then trying to access the same from an iframe but it shows me null. Iframe and parent window are not from the same domain. Is there any cross domain solution for this?

    1. You need to save data to localStorage inside iframe that’s on sub domain. That’s the only way to have cross-domain localStorage, you simlpy send message to the iframe to save data.

  5. hey,
    this seems great but in this case sub domain url is limited to just one. What if i have dynamic sub domains. How can i set dynamic sub domain url inside main file’s iframe’s src.

  6. On note – meanwhile this is a smart trick to set the cross-domain local storage between domain-subdomain, keep in mind that due to the security policy on iOS devices the local storage will be associated to the compound of (host site, iframe site). It means that when you open on iOS the iframe content (domain.com), the local storage will be empty for it – but when you open it within an iframe like you set it, the local storage will be there.

      1. If you realy need to make it work on iOS, you can use webscokets or firebase (that also use websockets) on both domain and subdomation and send data that way. But if you use websockets/firebase you will not need to use iframe anymore even for non iOS.

    1. Not sure if I’ve understood correctly but you should not store login tokens in Firebase since AFAIK you make db public (or part of it) to make it work from browser, I’m not sure if you can store token that can be accessed only by single user.

      Firebase can be used to have cross-domain storage, or if you want cross-domain events, just create item and delete it immediately.

      For login you should use Firebase auth. Not sure how this will work if you want to authenticate Cross-Domain.

    1. This solution is is one way communication, but it should not be hard to create double channel and and use promises to get the result back.
      The way it should be implement is like RPC, you send generated id and wait for same id back in other message.
      I may write POC with this when I have time. Using sysend library.

      1. Looking forward to that as well 🙂 Since it is an asynchronic call I am having problem picking up the ‘return value’. After the postmessage in domain a I having the script on domain b, where it actually reads the local storage put the value in that div. But by the time domain b is done with that, the code in domain a is already ahead and did not find the value since it was not there yet. Even a time out did not solve it. So I tried an even listener on domain a and have domain b do a postmessage to send the value. But no luck sofar in getting that synchronic, so still my code in domain a is already a few steps further

  7. If you’re only trying to share storage between subdomains (as opposed to entirely different domains), and you’re willing to risk setting the now-deprecated document.domain, then you don’t even need postMessage() and the infrastructure around it: Set document.domain = in all windows & iframes, and then you can access the window variables of the iframe from the main window… including its localStorage. This is even more useful if you want to use indexedDB instead, which has an API that’d be much harder to interact with via postMessage(). (Also SharedWorker, BroadcastChannel, etc – you can share all of them between subdomains this way). (See: https://stackoverflow.com/a/63602446/999120)

Leave a comment