Fix NGINX WordPress REST API Issues with Allow/Deny in Location Block

httphttp-headersnginxWordpress

I'm trying to restrict access to a WordPress REST API with NGINX (it's the main backend server {} block, no proxy):

location ~ ^/wp-json/ {
    allow x.x.x.x;
    deny all;
}

The problem is, that WordPress always returns a 404 when trying to access an API endpoint (www.example.com/wp-json/wp/v2/pages e.g.) with that configuration (no matter if it is the browser, which is passing all Cookies and Authorization stuff, or cURL):

curl -X GET -Ik https://www.example.com/wp-json/wp/v2/taxonomies
HTTP/2 404
date: Thu, 17 Jan 2019 12:40:12 GMT
content-type: application/json; charset=UTF-8
x-robots-tag: noindex
x-content-type-options: nosniff
access-control-expose-headers: X-WP-Total, X-WP-TotalPages
access-control-allow-headers: Authorization, Content-Type

But once I comment out the location block, I get no errors when I access the site with a browser.

I'm afraid that happens because I need to pass some HTTP headers for authorization purposes, as I have these HTTP headers in my REQUEST (checked with chrome devtools):

:authority: www.example.com
:method: GET
:path: /wp-json/wp/v2/taxonomies?context=edit&lang=en&_locale=user
:scheme: https
accept: application/json, */*;q=0.1
accept-encoding: gzip, deflate, br
accept-language: en-US,en;q=0.9
(!) ---> authorization: Basic crf344...fdfs334
cache-control: no-cache
(!) ---> cookie: wordpress_test_cookie=WP+Cookie+check; wordpress_logged_in_282...7da; wp-settings-1=mfo...off; wp-settings-time-1=1547726728
pragma: no-cache
referer: https://www.example.com/wp-admin/post.php?post=193&action=edit
(!) ---> x-wp-nonce: df238g3ds2

Could someone help me please in order to configure the location block properly for the WordPress REST API?

EDIT:

I managed to get a 200 with cURL if I add the corresponding HTTP headers:

curl -X GET -H "authorization: Basic c2F...TVY" \
     -H "cookie: wordpress_test_cookie=WP+...7da; \
                 wp-settings-1=mfo...off; \
                 wp-settings-time-1=1547726728"
     -H "x-wp-nonce: df238g3ds2" \
     -H "referer: https://www.example.com/wp-admin/post.php?post=193&action=edit" \
     -I https://www.example.com/wp-json/wp/v2/pages

How could I do the same with the NGINX location block?

Best Answer

as you requested, I'm posting my comments as an answer:

Instead of using regex (which could be a little slower) you could use a simple location. Also, since the logic to redirect the "pretty" permalinks to the correct PHP file is inside another location directive, you need to copy it to your new custom location:

location /wp-json/ {
    allow x.x.x.x;
    deny all;
    try_files $uri $uri/ /index.php?$args;
}

NOTE: you might need to adjust the actual try_files location