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?


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; \
     -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?

  • Why do you use location ~ ^/wp-json/ { instead of simply location /wp-json/ { ? No need to perform regex with that string (it is slower). Also, have you double checked in logs that the IP you are allowing is the same as the one you are using to reach the site?
    – NuTTyX
    Jan 17, 2019 at 13:37
  • @NuTTyX yes I checked the IP. Even if I delete all allow and deny statements I get a 404, if the block is commented out, it works. Thanks for your hint regarding the regex :)
    – manifestor
    Jan 17, 2019 at 13:54
  • Now that I think of it, you must have some directive to translate the short and readable uri into the proper php file in the wordpress. Check in other locations for parameters like try_files and try to replicate them inside your new location directive.
    – NuTTyX
    Jan 17, 2019 at 14:30
  • @NuTTyX yes, thank you! I missed a try_files $uri $uri/ /index.php?$args;. Post it as an answer, I want to give you some points! :)
    – manifestor
    Jan 17, 2019 at 14:51

1 Answer 1


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

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .