Browsersync and Inertia.

Tired of those XMLHttpRequest CORS policy errors? Me too.

Warrick Bayman
5 min readMar 4, 2020

Here’s a quick one on using Browsersync with Inertia. If you’ve ever tried and you got hung up on those pesky CORS errors that prevent redirects, then maybe I have a workaround for you.

The bit about Browsersync

Browsersync is one of those tools that just sticks. It has fantastic staying power. When I started building stuff for the web, LiveReload was all the rage. I loved that I could put my browser on one screen, my editor on another and not need to manually refresh every time I needed to see a change. It would just update whenever I saved stuff.

LiveReload eventually fell out of favour (is it even still a thing? I don’t know) and I went off to find an alternative. It didn’t take me long to come across Browsersync. It kinda did the same thing LiveReload did, but without the need to install that UI app. It’s a node dependency in the actual project, and it worked nicely with Webpack (also a recent discovery at the time).

Browsersync works by proxying your dev environment URL. Instead of visiting your site at whatever.test, you get a proxy url at localhost:3000. BrowserSync then automatically inserts a bit of JavaScript that does some magical things. Not only do you get an auto-reload when you save project files, but it will keep browsers in sync (hence the name) while you browse the site. Open two different browsers and point them both at localhost:3000. Now browse around your site and the other browser will follow along. It really is just like magic.

It’s become an invaluable piece of my toolkit and not one I’m going to give up without a fight.

The bit about Inertia

Inertia is (as you may have guessed) a JavaScript framework. It’s not just another Vue or React, but rather a compliment to those frameworks. Jonathan Reinink, who wrote Inertia, calls it the “modern monolith” and I think that’s a fairly apt description.

In very basic terms, Inertia replaces your Blade templates with Vue templates. You get all the benefits of a regular, multi-page, Laravel powered application, but with all the cool, reactive stuff that having a modern, single-page, Vue powered application brings. It means you don’t need to do SPA authentication (which is just the worse thing you’ll have to deal with when building SPAs) because Laravel will manage your sessions for you, but you get fancy JavaScripty stuff that makes you look cool.

Inertia is still pretty new, but I’ve used it in a bunch of places already and I find that I immediately miss it when I have to work on projects where Inertia wasn’t used.

The problem

So if Browsersync is fantastic, and Inertia is a little piece of Laravel developer heaven, what could possibly be the problem? Well, the problem isn’t really a problem at all. It’s actually a good thing. It’s a simple piece of security baked into all our browsers called CORS.

CORS (Cross Origin Request Sharing) is designed to help prevent cross-site request forgeries. It’s intention to either give or deny an application running at one domain access to the resources of an application running at a very different domain.

As a security measure browsers prevent JavaScript applications from making requests across origins. When working with Browsersync, you’re actually working with two different origins. Your dev environment URI ( whatever.test ) and the proxy URI served by Browsersync ( localhost:3000 ). From the browsers point of view, that’s two distinct origins. The result is the following:

CORS policy ruing your day

It’s frustrating, but it’s also kind of a good thing. You don’t really want to break this stuff because it’s actually going to make your life easier later on.

The quick solution

So there are two solutions to this. The first thing that you might come across is why not just set the CORS headers to allow the different origins access to each others resources? That’s not a bad idea, but if you don’t have any intention of making cross origin requests outside of your dev environment, then this feels a little like breaking the wheel, then re-inventing the wheel, and then changing the wheel again before going into production.

Setting up CORS correctly takes a bit of time and can be a little tricky. There are a few packages around that attempt to make the process easier, but you’ll need to go read up a bunch about CORS first. Note that I’m not advocating for ignoring CORS at all. You definitely should go and learn about what CORS is and why it’s causing these errors in the first place, but sometimes I just don’t think it’s necessary to change the browser defaults.

Instead, there is a really simple solution and is requires two tiny changes.

First, update your Browsersync config and add the open option and set it to false. If you’re a Laravel Mix user, you can do this with:

webpack.mix.js

If you’re not a mix user, I’m sure you can translate the above into whatever you’re using. The Browsersync options are the same regardless.

To be honest, this first change isn’t all that necessary, but it will stop the browser from opening automatically which gets a little annoying eventually. Especially when your default browser isn’t the one you use for development (yes, I’m weird like that).

Next, add this small addition to your app.blade.php file that you created when you were setting up Inertia.

app.blade.php

And here’s the really big change… Don’t visit http://localhost:3000 anymore and instead use your dev URL. So http://whatever.test (or whatever yours happens to be).

You’ll still get all that magical Browsersync goodness, and you can continue to use Inertia as before. There’s no need to go fiddling with CORS headers (which would have been a much bigger, and more time consuming change).

The app()->isLocal() means you don’t need to worry about it being used in production so you can just leave it there. As long as you’re environment isset to local and you have Browsersync watching your project, you can carry on as normal.

--

--

Warrick Bayman

Programmer, musician, cyclist (well... I own a bike), husband and father.