Access-Control-Allow-Origin server mods are not working - wordpress

I am having a terrible time trying to get my server to accept requests from another server (local, but given a domain name in my hosts file) without triggering the dreaded
XMLHttpRequest cannot load https://dev.mydomain.org/api/user?uid=1. Origin http://home.domain.org is not allowed by Access-Control-Allow-Origin.
my dev server (internet) is running nginx, my home server(local) is running apache.
I have tried several solutions found on the internet, to no avail. I have tried modifying the headers in the nginx configs to allow my home.mydomain.org server, I have also added htaccess rules locally to allow all origins (*).
My nginx server block has these lines currently:
add_header Access-Control-Allow-Origin http://home.mydomain.org;
add_header Access-Control-Allow-Headers Authorization;
Adding just the first one did change my response slightly (from simple Origin not allowed by Access-Control-Allow-Origin to Request header field Authorization is not allowed by Access-Control-Allow-Headers.) but adding the second line just reverted the error to the original one and I am still blocked.
At this point, I am not sure what else to try.
UPDATES:
Launching Chrome with flag --disable-web-security allows me to test, and my site and code is working fine in Chrome.
However, this revealed another strange problem, which is that if I try adding the add_header lines to a location directive, both my no-web-security Chrome and my unmodified Safari fail to load info from my api. So now I am not sure if my add_header directives in the server block are working correctly at all.
If it helps any, here is my client code (including things I have tried/commented out):
var xhr = new XMLHttpRequest();
var self = this;
xhr.open('GET', apiURL + self.currentIssue);
xhr.setRequestHeader('Access-Control-Allow-Origin','http://home.mydomain.org');
//xhr.setRequestHeader('Access-Control-Allow-Credentials', 'true');
xhr.withCredentials = true;
//xhr.setRequestHeader('Access-Control-Request-Method','*');
xhr.setRequestHeader('Authorization','Bearer longstringoflettersandnumbers');
xhr.onload = function () {
self.posts = JSON.parse(xhr.responseText);
};
xhr.send();
ANOTHER UPDATE AFTER TRYING SUGGESTION BELOW:
After a bunch of trial and error on both client and server, I still am stuck. Here is my latest response from the server using curl (although I have toggled on and off various options client and server for things like Credentials and changing origin to exactly mine or * to no avail):
HTTP/1.1 204 No Content
Server: nginx
Date: Sun, 06 Aug 2017 10:11:57 GMT
Connection: keep-alive
Access-Control-Allow-Origin: http://home.mydomain.org
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Authorization,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range
Access-Control-Max-Age: 1728000
Content-Type: text/plain; charset=utf-8
Content-Length: 0
and here are my console errors (Safari):
[Error] Origin http://home.mydomain.org is not allowed by Access-Control-Allow-Origin.
[Error] Failed to load resource: Origin http://home.mydomain.org is not allowed by Access-Control-Allow-Origin. (actions, line 0)
[Error] XMLHttpRequest cannot load https://dev.mydomain.org/api/user?uid=1 due to access control checks.
And here is my console error for Firefox:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://dev.mydomain.org/api/user?uid=1. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).
Also in Firefox, here are the results from the network panel for OPTIONS and GET:
Request URL: https://dev.mydomain.org/api/user?uid=1
Request method: OPTIONS
Status code: 204 No Content
Version: HTTP/2.0
Response headers (511 B)
Server "nginx"
Date "Sun, 06 Aug 2017 10:44:22 GMT"
Access-Control-Allow-Origin "http://home.mydomain.org"
access-control-allow-credentials "true"
Access-Control-Allow-Methods "GET, POST, OPTIONS"
Access-Control-Allow-Headers "Authorization,DNT,X-CustomHea…ent-Type,Content-Range,Range"
Access-Control-Max-Age "1728000"
Content-Type "text/plain; charset=utf-8"
Content-Length "0"
X-Firefox-Spdy "h2"
Request headers (501 B)
Host "dev.mydomain.org"
User-Agent "Mozilla/5.0 (Macintosh; Intel… Gecko/20100101 Firefox/54.0"
Accept "text/html,application/xhtml+x…lication/xml;q=0.9,*/*;q=0.8"
Accept-Language "en-US,en;q=0.5"
Accept-Encoding "gzip, deflate, br"
Access-Control-Request-Method "GET"
Access-Control-Request-Headers "authorization"
Origin "http://home.mydomain.org"
Connection "keep-alive"
Cache-Control "max-age=0"
Request URL: https://dev.mydomain.org/api/user?uid=1
Request method: GET
Status code: 404 Not Found
Version: HTTP/2.0
Response headers (170 B)
Server "nginx"
Date "Sun, 06 Aug 2017 10:44:22 GMT"
Content-Type "text/html"
Vary "Accept-Encoding"
Content-Encoding "gzip"
X-Firefox-Spdy "h2"
Request headers (723 B)
Host "dev.mydomain.org"
User-Agent "Mozilla/5.0 (Macintosh; Intel… Gecko/20100101 Firefox/54.0"
Accept "*/*"
Accept-Language "en-US,en;q=0.5"
Accept-Encoding "gzip, deflate, br"
Referer "http://home.mydomain.org/"
Authorization "Bearer eyJ0eXAG…BRHmX9VmtYHQOvH7k-Y32wwyeCdk"
Origin "http://home.mydomain.org"
Connection "keep-alive"
Cache-Control "max-age=0"
UPDATE WITH PARTIAL SUCESS:
I think I found the problem (partially): changing my location directive in nginx from location /api to location = /api/* gets it working! But only for Safari and Chrome, FF is now not even trying the GET request, there is NO entry for it in network panel.
UPDATE WITH CRYING AND GNASHING OF TEETH AND PULLING OF HAIR
Safari and Chrome intermittently fail with original error about Origin not allowed, even though they were working fine and no changes have been made to server config. I will be drinking heavily tonight...

Wow, was that ever convoluted. Posting answer here in case some other WP user finds their way here. I kept getting inconsistent results (sometimes working, sometimes not mysteriously) and finally tracked down my problem to headers being set in the PHP code on the server, independently of the nginx settings and sometimes contradicting them (although never in a predictable way that I could see). So the things I needed to resolve were:
Removed all my cors declarations in my nginx configs
I also have code on my server that validates a token in the auth header, and it was failing on OPTIONS preflight (which it should never check) so I had to add an if statement before to have it ignore an OPTIONS call (!$_SERVER['REQUEST_METHOD'] === "OPTIONS")
Since I had cloned this site from another of mine using UpdraftPlus plugin, I had to go in to delete my migrate keys since their existence prevented api calls from working too. Once they were deleted my calls started working again.
Removed and re-added the built in WP filter rest_pre_serve_request
My filter code is here:
add_action('rest_api_init', function() {
/* unhook default function */
remove_filter('rest_pre_serve_request', 'rest_send_cors_headers');
/* then add your own filter */
add_filter('rest_pre_serve_request', function( $value ) {
$origin = get_http_origin();
$my_sites = array( $origin ); // add array of accepted sites if you prefer
if ( in_array( $origin, $my_sites ) ) {
header( 'Access-Control-Allow-Origin: ' . esc_url_raw( $origin ) );
} else {
header( 'Access-Control-Allow-Origin: ' . esc_url_raw( site_url() ) );
}
header( 'Access-Control-Allow-Methods: OPTIONS, GET, POST, PUT, PATCH, DELETE' );
header( 'Access-Control-Allow-Credentials: true' );
header('Access-Control-Allow-Headers: Authorization,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Origin,Content-Type,X-Auth-Token,Content-Range,Range');
header('Access-Control-Expose-Headers: Authorization,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Origin,Content-Type,X-Auth-Token,Content-Range,Range');
header( 'Vary: Origin' );
return $value;
});
}, 15);
Now finally, everything works everywhere (in every browser and in curl too)!

Related

Specific location path is not refreshing

We have a OpenResty, Nginx and Redis stack. I have a location block in my nginx.conf which doesn't reponse to any changes I do.
I have a Lua module function that is invoked by location in nginx.conf. The function is like:
function _M.myFunction()
ngx.header["Content-Type"] = "text/html"
ngx.say("debug")
end
I have a location like this - this location was set to another function that returned text/plain Content-Type with an empty string (ngx.say(" ") and I now pointed it to my debug function, it seems that it is "stuck" on previous response:
location /.faulty/foo/{
content_by_lua_block {
myLuaModule:myFunction()
}
}
responding with:
HTTP/1.1 200 OK
Content-Type: application/octet-stream
Transfer-Encoding: chunked
Connection: keep-alive
with no body in response.
Testing by adding a different locations seems to working great for the same function and getting me the response I want:
location /.test/foo/{
content_by_lua_block {
myLuaModule:myFunction()
}
}
responding with:
HTTP/1.1 200 OK
Content-Type: text/html
Connection: keep-alive
debug
I checked Redis for any key that might hold this location, and couldn't find anything relevant.
so, for future generations -
there was a LB server in front of that server with the exact same function and location.
once changed there the change was refreshed.

Why does my Web API return 404 on all resources when called from a Console app?

I have a little Web API, almost directly from the standard VS project template, i.e. Home and Values controllers, and lots of MVC cruft. It is set to run and debug under IIS 10.
I have set up tracing by adding the package Microsoft.AspNet.WebApi.Tracing and the following code in WebApiConfig:
public static void Register(HttpConfiguration config)
{
config.EnableSystemDiagnosticsTracing();
SystemDiagnosticsTraceWriter traceWriter = config.EnableSystemDiagnosticsTracing();
traceWriter.IsVerbose = true;
traceWriter.MinimumLevel = TraceLevel.Debug;
config.Services.Replace(typeof(ITraceWriter), new SimpleTracer());
...
...
}
SimpleTracer is an ITraceWriter that writes to a text file.
When I call the API from outside the VS ecosystem, i.e. from PostMan in Chrome, a bad url, that results in a 404 error message, and the creation of a new trace file if there's not already one. Of I call it from PostMan with a good url, I get the expected result, and a trace of the request in the trace file.
When I call it from my Console app, even with a good url, I still get a 404 error response, and nothing is written to the trace file. I made sure by removing it and IIS doesn't even re-create it when using the .exe client.
If I call it from the compiled .exe from outside VS, I get the same error.
Then, when I set the Web API to use IIS Express, everything works perfectly. Do I need CORS for calls from non-web apps, does IIS need an extra header in this case? What is wrong?
EDIT A: This is the request when I use PostMan, and it returns a 200 and the expected list of strings.
GET /DemoApi/api/values HTTP/1.1
Host: localhost
Content-Type: application/json
Cache-Control: no-cache
Postman-Token: f9454ffc-6a8d-e1ed-1a28-23ed8166e534
and the response and headers:
["value1","value2","value3","value4","value5","value6","value7"]
Cache-Control →no-cache
Content-Length →64
Content-Type →application/json; charset=utf-8
Date →Tue, 13 Dec 2016 06:20:07 GMT
Expires →-1
Pragma →no-cache
Server →Microsoft-IIS/10.0
X-AspNet-Version →4.0.30319
X-Powered-By →ASP.NET
EDIT B: This is the request sent using HttpClient:
GET http://abbeyofthelema/api/values HTTP/1.1
Accept: application/json
Host: abbeyofthelema
Connection: Keep-Alive
The only real difference is that because Fiddler doesn't capture traffic from localhost, I had to use my computer name instead. The same recipient still gets the request.
The response here is:
HTTP/1.1 404 Not Found
Cache-Control: private
Content-Type: text/html; charset=utf-8
Server: Microsoft-IIS/10.0
X-Powered-By: ASP.NET
Date: Tue, 13 Dec 2016 08:07:25 GMT
Content-Length: 4959
According to Timothy Shields at the following link
Why is HttpClient BaseAddress not working?
You must place a slash at the end of the BaseAddress, and you must not place a slash at the beginning of your relative URI

Nginx return statement not accepting "text"

Following config is working for me:
server {
listen 80;
root /app/web;
index index.json;
location / {
return 409;
}
}
If I hit the website the 409 page will be presented. However following is not working:
server {
listen 80;
root /app/web;
index index.json;
location / {
return 409 "foobar";
}
}
The page is unreachable. But according to the docs http://nginx.org/en/docs/http/ngx_http_rewrite_module.html#return
return 409 "foobar";
should work. Any ideas whats wrong? There are no logs in nginx/error.log.
The thing is, Nginx does exactly what you ask it to do. You can verify this by calling curl -v http://localhost (or whatever hostname you use). The result will look somewhat like this:
* Rebuilt URL to: http://localhost/
* Hostname was NOT found in DNS cache
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 80 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.35.0
> Host: localhost
> Accept: */*
>
< HTTP/1.1 409 Conflict
* Server nginx/1.4.6 (Ubuntu) is not blacklisted
< Server: nginx/1.4.6 (Ubuntu)
< Date: Fri, 08 May 2015 19:43:12 GMT
< Content-Type: application/octet-stream
< Content-Length: 6
< Connection: keep-alive
<
* Connection #0 to host localhost left intact
foobar
As you can see, Nginx returns both 409 and foobar, as you ordered.
So the real question here is why your browser shows the pretty formatted error page when there is no custom text after the return code, and the gray "unreachable" one, when such text is present.
And the answer is: because of the Content-Type header value.
The HTTP standard states that some response codes should or must come with the response body. To comply with the standard, Nginx does this: whenever you return a special response code without the required body, the web server sends its own hardcoded HTML response to the client. And a part of this response is the header Content-Type: text/html. This is why you see that pretty white error page, when you do return 409 without the text part — because of this header your browser knows that the returned data is HTML and it renders it as HTML.
On the other hand, when you do specify the text part, there is no need for Nginx to send its own version of the body. So it just sends back to the client your text, the response code and the value of Content-Type that matches the requested file (see /etc/nginx/mime.types).
When there is no file, like when you request a folder or a site root, the default MIME type is used instead. And this MIME type is application/octet-stream, which defines some binary data. Since most browsers have no idea how to render random binary data, they do the best they can, that is, they show their own hardcoded error pages.
And this is why you get what you get.
Now if you want to make your browser to show your foobar, you need to send a suitable Content-Type. Something like text/plain or text/html. Usually, this can be done with add_header, but not in your case, for this directive works only with a limited list of response codes (200, 201, 204, 206, 301, 302, 303, 304, or 307).
The only other option I see is to rewrite your original request to something familiar to Nginx, so that it could use a value from /etc/nginx/mime.types for Content-Type:
server {
listen 80;
root /app/web;
index index.json;
location / {
rewrite ^.*$ /index.html;
return 409 "foobar";
}
}
This might seem somewhat counter-intuitive but this will work.
EDIT:
It appears that the Content-Type can be set with the default_type directive. So you can (and should) use default_type text/plain; instead of the rewrite line.
Updating #ivan-tsirulev 's answer:
By now you can set headers even for page with status codes for errors using always.
location #custom_error_page {
return 409 "foobar";
add_header Content-Type text/plain always;
}
But if you set default_type, the response headers will have two Content-Type headers: default, then added. Nevertheless, it works fine.

nginx - read custom header from upstream server

I am using nginx as a reverse proxy and trying to read a custom header from the response of an upstream server (Apache) without success. The Apache response is the following:
HTTP/1.0 200 OK
Date: Fri, 14 Sep 2012 20:18:29 GMT
Server: Apache/2.2.17 (Ubuntu)
X-Powered-By: PHP/5.3.5-1ubuntu7.10
Connection: close
Content-Type: application/json; charset=UTF-8
My-custom-header: 1
I want to read the value from My-custom-header and use it in a if clause:
location / {
// ...
// get My-custom-header value here
// ...
}
Is this possible?
It's not only possible, it's easy:
in nginx the response header values are available through a variable (one per header).
See http://wiki.nginx.org/HttpCoreModule#.24sent_http_HEADER for the details on those variables.
In your examle the variable would be $sent_http_My_custom_header.
I was facing the same issue. I tried both $http_my_custom_header and $sent_http_my_custom_header but it did not work for me.
Although solved this issue by using $upstream_http_my_custom_header.
When using NGINX as a proxy, there are four sets of headers:
client -> nginx: the client request headers
nginx -> upstream: the upstream request headers
upstream -> nginx: the upstream response headers
nginx -> client: the client response headers
You appear to be asking about the upstream response headers. Those are found in the $upstream_http_name variables.
However, take into account that any response headers are only set after the headers from the upstream server response have been received. Any if directives are run before sending the upstream request, and will not have access to any response headers! In other words, if directives are run after the client request has been received, before making the upstream request.
If you need to change how a response is handled, you can use a map directive however to set variables based on response headers, then use those variables in add_header (set client response headers), log_format or any othere directives that are active during the response phases (internally named the NGX_HTTP_CONTENT_PHASE and NGX_HTTP_LOG_PHASE phases). For more complex control you'll have to use a scripting add-on such as the Lua module (e.g. using a header_filter_by_lua_block directive).
To read or set individual headers, use:
from
to
type
read (variable)
write (directive)
client
nginx
request
$http_name
–
ngnix
upstream
request
–
proxy_set_header
upstream
nginx
response
$upstream_http_name
–
nginx
client
response
$sent_http_name
add_header
NGINX copies certain headers from client request to upstream request, and from upstream response to client response using various proxy_ directives, giving you options to omit or explicitly include headers for either direction. So if an upstream response header is only found in $upstream_http_name variables, then those headers were specifically not copied to the client response, and the set of available $sent_http_name variables will include any extra headers set by NGINX that are not present in the upstream response.
Use $http_MY_CUSTOM_HEADER
You can write some-thing like
set my_header $http_MY_CUSTOM_HEADER;
if($my_header != 'some-value') {
#do some thing;
}

Compojure: getting the body from a POST request from which the Content-Type header was missing

Given this snippet:
(defroutes main-routes
(POST "/input/:controller" request
(let [buff (ByteArrayOutputStream.)]
(copy (request :body) buff)
;; --- snip
The value of buff will be a non-empty byte array iff there's the Content-Type header in the request. The value can be nonsencial, the header just has to be there.
However, I need to dump the body (hm... that came out wrong) if the request came without a content type, so that the client can track down the offending upload. (The uploading software is not under my control and its maintainers won't provide anything extra in the headers.)
Thank you for any ideas on how to solve or work around this!
EDIT:
Here are the headers I get from the client:
{
"content-length" "159",
"accept" "*/*",
"host" (snip),
"user-agent" (snip)
}
Plus, I discovered that Ring, using an instance of Java's ServletRequest, fills in the content type with the standard default, x-www-form-urlencoded. I'm now guessing that HTTPParser, which supplies the body through HTTPParser#Input, can't parse it correctly.
I face the same issue. It's definitely one of the middleware not being able to parse the body correctly and transforming :body. The main issue is that the Content-Type suggest the body should be parsable.
Using ngrep, I found out how curl confuses the middleware. The following, while intuitive (or rather sexy) on the command line sends a wrong Content-Type which confuses the middleware:
curl -nd "Unknown error" http://localhost:3000/event/error
T 127.0.0.1:44440 -> 127.0.0.1:3000 [AP]
POST /event/error HTTP/1.1.
Authorization: Basic SzM5Mjg6ODc2NXJkZmdoam5idmNkOQ==.
User-Agent: curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3.
Host: localhost:3000.
Accept: */*.
Content-Length: 13.
Content-Type: application/x-www-form-urlencoded.
.
Unknown error
The following however forces the Content-Type to being opaque and the middleware will not interfere with the :body.
curl -nd "Unknown error" -H "Content-Type: application/data" http://localhost:3000/event/error
T 127.0.0.1:44441 -> 127.0.0.1:3000 [AP]
POST /event/error HTTP/1.1.
Authorization: Basic SzM5Mjg6ODc2NXJkZmdoam5idmNkOQ==.
User-Agent: curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3.
Host: localhost:3000.
Accept: */*.
Content-Type: application/data.
Content-Length: 13.
.
Unknown error
I'm considering replacing the middleware with a more liberal one because even though the request is wrong, I'd still like to be able to decide what to do with the body myself. It's a really weird choice to zero the request body when the request doesn't make sense. I actually think a more correct behavior would be to pass it to an error handler which by default would return a 400 Bad Request or 406 Not Acceptable.
Any thoughts on that? In my case I might propose a patch to Compojure.
According to:
http://mmcgrana.github.com/ring/ring.middleware.content-type-api.html
the default content type is application/octet-stream. Unless you actively support that content type, can't you just check if the content type matches that one, and then dump whatever you need based on that?

Resources