# Add CORS header for WP Rest API in nginx-includes with a group_vars variable

**URL:** https://discourse.roots.io/t/add-cors-header-for-wp-rest-api-in-nginx-includes-with-a-group-vars-variable/15352
**Category:** trellis
**Tags:** nginx, ansible
**Created:** 2019-04-19T12:57:59Z
**Posts:** 4

## Post 1 by @Twansparant — 2019-04-19T12:57:59Z

Hi there,

On a running project we’re using Wordpress as a **headless CMS** using only the **WP Rest API** to get data in a **React frontend** using [Next.js](https://nextjs.org). For the frontend we’re using Docker Compose with **Gitlab CI** for deployment and for the backend **Trellis**.

The frontend & backend are available on 3 environments, on (as example) these domains:

```
http://mydomain.test => http://admin.mydomain.test 
https://staging.mydomain.io => http://admin.staging.mydomain.io
https://mydomain.io => http://admin.mydomain.io
```

Each Trellis server should only be allowed to return the WP Rest API get requests from the matching environment, so I added a variable name in every **group\_vars** environment called `front_url`:

```
wordpress_sites:
  admin.mydomain.io:
    site_hosts:
      - canonical: admin.mydomain.test
        redirects:
          - www.admin.mydomain.test
    local_path: ../site
    multisite:
      enabled: false
    ssl:
      enabled: false
      provider: self-signed
    cache:
      enabled: false
    env:
      front_url: http://mydomain.test
      front_domain: mydomain.test
```

In my headless WP theme, I set the **CORS header** based upon this env var:

```
function get_frontend_origin() {
	return FRONT_URL;
}

add_action( 'rest_api_init', function() {

	remove_filter('rest_pre_serve_request', 'rest_send_cors_headers');

	add_filter('rest_pre_serve_request', function($value) {
		header('Access-Control-Allow-Origin: ' . get_frontend_origin());
		header('Access-Control-Allow-Methods: GET');
		header('Access-Control-Allow-Credentials: true');

		return $value;
	});
}, 15);
```

Which works great!

Now my problem started when I needed an extra subdomain on my frontend that should also be able to use my WP Rest API:

```
http://mydomain.test,
http://sub.mydomain.test => http://admin.mydomain.test
https://sub.staging.mydomain.io,
https://staging.mydomain.io => http://admin.staging.mydomain.io
https://sub.mydomain.io,
https://mydomain.io => http://admin.mydomain.io
```

Since it’s not possible to use wildcards in the **Access-Control-Allow-Origin** header, I tried to use the  
`$_SERVER['HTTP_ORIGIN']` value and check that against an array of allowed domains, but for some reason the `$_SERVER['HTTP_ORIGIN']` value is always empty?

So then I thought, I will remove these CORS settings in PHP and will try to add it in a **nginx-includes** template in Trellis and bumped into [this example](https://gist.github.com/Stanback/7145487) to set the headers based upon the `$http_origin`.

So I added this structure in the root of my trellis folder:

```
trellis
└──nginx-includes/
     └── admin.mydomain.io/
          └── cors.conf.j2
```

With the following rules in **cors.conf.j2** :

```
location ~ ^/wp-json/ {
	set $cors "";

	if ($http_origin ~* (.*\.{{ site.env.front_domain }})) {
		set $cors "true";
	}

	if ($cors = "true") {
		add_header 'Access-Control-Allow-Origin' "$http_origin";
		add_header 'Access-Control-Allow-Credentials' 'true';
		add_header 'Access-Control-Allow-Methods' 'GET';
		add_header 'Access-Control-Allow-Headers' 'User-Agent,Keep-Alive,Content-Type';
	}
}
```

But a re-provision results in an error:

```
AnsibleUndefinedVariable: 'site' is undefined
```

So my main question is, how can I use my `front_domain` variable from **group\_vars** in my **nginx-includes Jinja2** template like this?

Since the template is already in it’s separate site folder, how can I access it’s **group\_vars**?  
Any help is welcome, thanks!

---

## Post 2 by @Twansparant — 2019-04-19T13:54:50Z

Ok by writing my post I realized what I did wrong here, in order to get the site specific group\_vars I need to use a **child template** , so I added the following file:

```
trellis
└──nginx-includes/
     └── admin.mydomain.io.conf.child
```

and added the file in my **group\_vars** :

```
wordpress_sites:
  admin.mydomain.io:
    nginx_wordpress_site_conf: nginx-includes/admin.mydomain.io.conf.child
    site_hosts:
      - canonical: admin.mydomain.test
        redirects:
          - www.admin.mydomain.test
    local_path: ../site
    multisite:
      enabled: false
    ssl:
      enabled: false
      provider: self-signed
    cache:
      enabled: false
    env:
      front_url: http://mydomain.test
      front_domain: mydomain.test
```

My child template file looks like this now:

```
{% extends 'roles/wordpress-setup/templates/wordpress-site.conf.j2' %}

{% block h5bp -%}
  {{ super() }}

  location ~ ^/wp-json/ {
    set $cors "";

    if ($http_origin ~* (.*\.{{ item.value.env.front_domain }})) {
      set $cors "true";
    }

    if ($cors = "true") {
      add_header 'Access-Control-Allow-Origin' "$http_origin";
      add_header 'Access-Control-Allow-Credentials' 'true';
      add_header 'Access-Control-Allow-Methods' 'GET';
      add_header 'Access-Control-Allow-Headers' 'User-Agent,Keep-Alive,Content-Type';
    }
  }
{% endblock %}
```

When I re-provision and check my `/etc/nginx/sites-available/admin.mydomain.io.conf` file, it prints the **item.value.env.front\_domain** value correctly:

```
location ~ ^/wp-json/ {
    set $cors "";

    if ($http_origin ~* (.*\.mydomain.test)) {
      set $cors "true";
    }

    if ($cors = "true") {
      add_header 'Access-Control-Allow-Origin' "$http_origin";
      add_header 'Access-Control-Allow-Credentials' 'true';
      add_header 'Access-Control-Allow-Methods' 'GET';
      add_header 'Access-Control-Allow-Headers' 'User-Agent,Keep-Alive,Content-Type';
    }
  }
```

But now, all my WP Rest API requests result in an **404 Not Found** page…  
Any idea what I’m doing wrong here?

Thanks!

---

## Post 3 by @Twansparant — 2019-04-26T09:14:44Z

> [@Twansparant](#):
>
> but for some reason the `$_SERVER['HTTP_ORIGIN']` value is always empty

Ok, in case anyone else bumps into this;

The reason why `$_SERVER['HTTP_ORIGIN']` was empty, was because I was making the **WP Rest API** request on the server side in **next.js** , so the origin was empty.

If I pass the origin in the **headers** object:

```
import fetch from 'isomorphic-unfetch';

// passed from express
const origin = req.protocol + '://' + req.get('host');
let page;

await fetch(`https://admin.mydomain.io/wp-json/my-app/my-endpoint`, {
	headers: { origin }
})
.then(result => result.json())
.then(result => {
	page = result;
}).catch(error => {
	console.log(error)
});
```

I can make the check in **Wordpress** :

```
add_action('rest_api_init', function() {

	remove_filter('rest_pre_serve_request', 'rest_send_cors_headers');
	add_filter('rest_pre_serve_request', function($value) {

		// Get http origin
		$origin = get_http_origin();
		$domains = [
			$_SERVER['FRONT_URL'], // Set in .env file
			$_SERVER['SUBDOMAIN_URL'] // Set in .env file
		];

		if ($origin && in_array($origin, $domains)) {
			header( 'Access-Control-Allow-Origin: ' . esc_url_raw($origin));
			header( 'Access-Control-Allow-Methods: GET, OPTIONS');
			header( 'Access-Control-Allow-Credentials: true');
		}

		return $value;
	});
}, 15);
```

Now my WP Rest API only accepts GET requests from my pre-defined (sub)domains!

---

## Post 4 by @craigpearson — 2019-04-26T11:12:23Z

Thanks a lot for sharing this, I’m about to hit the same issue on a multisite setup so this gives me a great head start!

---

## Post 5 by @system — 2019-05-31T12:58:00Z

This topic was automatically closed after 42 days. New replies are no longer allowed.
