Local Community

Adding CORS policy to local site

Hello there, I realize this is an edge case, but I’m wondering if there is a way to add a CORS policy to the nginx config file for either the full site, or alternately the /wp-json/ portion of the site?

Backstory: I am running a React app locally using a project that was bootstrapped with Create React App, so I’m pretty much tied to this environment. In production, CORS will not be an issue because the React app will run on the same domain and port number as the website. However, in development mode it will run on http://localhost:3000. This is a requirement, and although I can run it using my Local site domain, the port number still needs to be 3000 or a unique number of my choosing - point is, it cannot be the same port number as Local (e.g. https://mylocaladdress.local:3000).

I’ve tried multiple ways, following directions on Enable CORS and other sites that show the same code. But when adding this to the site.conf.hbs file and restarting the server I get the typical 502 Request Error message, so this is either conflicting with global code or just not written correctly for the Local nginx environment. I’ve also tried to add code as found on this website, but again it doesn’t work because I believe it’s checking the CORS policy on the server-level before even hitting this code.

I have bypassed this for the time being by adding a Chrome extension called “Allow CORS: Access-Control-Allow-Origin” however I fear this could expose me to security vulnerabilities if I forget to disable this while navigating to other websites. So I’m looking for an elegant solution, and I believe editing the nginx configuration files would be that solution. I appreciate any insights!

Local version 5.7.5+4909
macOS Catalina version 10.15.7
Nginx web server
PHP 7.4.1
MySQL 8.0.16
WP 5.5.1

This is an interesting issue you’ve encountered!

If I understand things correctly, you should be able to do development if the Local site is running on localhost – is that correct?

If so, can you try updating Local’s “Router Mode” to localhost from “Preferences > Advanced” and see if that gets you closer to what you need?

Basically what this setting does is bypass Local’s router and allows you to access the WordPress site directly on the port (something like localhost:10012)

For more info on the “Router Mode” setting, see: https://localwp.com/help-docs/router-mode/

Thanks Ben, this seemed to work! At least it’s a temporary solution so I can remove that Chrome plugin and browse the web safely. I still believe there’s a more elegant solution within the nginx config files, and perhaps this conversation should be moved into the Feature reqeusts category. I’m not sure if it’s a limitation of local development, or if it’s just something that hadn’t been considered, but I do think this feature could aide in deeper integrations, especially by those of us who use Flywheel hosting and want to ensure our local environment is as close to production as possible. Thanks again, and if you have any further insights into making changes within the nginx config files that might help me, I’m all ears :ear: :slight_smile:

[EDIT] My apologies, I spoke too soon, and didn’t test properly. Even with my React app using localhost:3000 and Local using localhost:10003 I am running into the same error: Access to XMLHttpRequest at ‘http://localhost:10003/wp-json/api/endpoint’ from origin ‘http://localhost:3000’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. So, again, I wonder if there’s an elegant way to do this within nginx config?

That’s intersting. I would think that this should be working under localhost router mode. For background info, the nginx config for the individual Local site is located at:

~/Local Sites/<site-name>/conf/nginx/site.conf.hbs

When I look at the configuration for a recently created site, I do see a header that appears to account for CORS, but I think it’s restricted to only certain file extensions.

Maybe you can update the location regex, or possible create a new location block for the JSON endpoint?

Here’s a screenshot to help visualize:

Since that file is a handlebars file (.hbs) you’ll need to make the changes to that configuration file and then stop the site in Local and re-start it so that the configuration is re-compiled.

The reason that there is this compilation step is that Local has to do different things for the different OSes (Windows and everyone else)

Hi Ben, thanks for the insights you presented. After a lot of trial and error - hours, in fact - I was able to figure out a way to do this within my development environment!

The reason CORS was not working was due to the port numbers, regardless if it was the same domain or not. So I did have to add the Access-Control-Allow-Origin header and assign the wildcard. But the questions that took me forever to figure out was “where” and “how.”

I kept trying to make location blocks like this: location / { … } or location ~ /wp-json/ { … } but that would result in 404 errors and 502 errors. I figured out that this wasn’t the right direction since nearly all of WordPress relies on the index.php file - duh! So that made me switch gears into the location block that was already set up for PHP files: location ~ .php$ { … }

Once I figured that out, I started playing around with the Enable CORS code that I had found earlier. Most of that was either not necessary or too restrictive, and I also kept running into issues where the AJAX preflight requests were being denied because for some reason it wasn’t seeing the ‘Access-Control-Allow-Origin’ ‘*’ header. So after much Googling and combining ideas, this is what I ended up putting at the bottom of the location ~ .php$ { … } block:

# allow CORS requests
if ($request_method = 'OPTIONS') {
	add_header 'Access-Control-Allow-Origin' '*' always;
	add_header 'Access-Control-Allow-Methods' '*' always;
	add_header 'Access-Control-Allow-Headers' '*' always;
	add_header 'Access-Control-Max-Age' 1728000;
	add_header 'Content-Type' 'text/plain; charset=utf-8';
	add_header 'Content-Length' 0;
	return 200;
}
if ($request_method = 'POST') {
	add_header 'Access-Control-Allow-Origin' '*' always;
	add_header 'Access-Control-Allow-Methods' '*' always;
	add_header 'Access-Control-Allow-Headers' '*' always;
}
if ($request_method = 'GET') {
	add_header 'Access-Control-Allow-Origin' '*' always;
	add_header 'Access-Control-Allow-Methods' '*' always;
	add_header 'Access-Control-Allow-Headers' '*' always;
}

The addition of the “always” at the end I assume ensures that these headers take priority in every request, almost like !important clause in CSS. This allowed all requests to work, including the AJAX preflight requests!

Is this ugly? Sure. Is it unsafe? Yes, and should only be used within the Local environment. It’s definitely not for production environments… But it works!

Thank you again for your assistance!

1 Like

This is awesome! Thanks for hacking away at this problem and circling back here to the forums to share what you found!

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.