res.setHeaders is used to set cache in headers but how to get x-cache and other from headers?
I'm deploying my web app to heroku and use the following cors config on the client side.
const instance = axios.default.create({
baseURL: "https://myapp-backend.herokuapp.com",
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
"Access-Control-Allow-Origin": "https://myapp-backend.herokuapp.com"
}
});
And on the server side
const corsConfig = {
origin: ['https://myapp.herokuapp.com', 'http://localhost:3001']
}
app.use(cors(corsConfig));
But I keep getting error: "Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource."
Any help would be much appreciated. Sometimes the config works but sometimes it doesn't...
The custom headers on your request (some of which are misguided) have triggered a pre-flight request for the cross origin request. This means that the browser will separately send an OPTIONS request to your server to ask it if it is OK to process this request before sending the actual request.
You have a couple of choices. First, remove the custom headers that are triggering the pre-flight request. Neither Content-Type or Access-Control-Allow-Origin are appropriate for a request you are sending from the browser. Content-Type belongs to the response and the client does not get to specify anything about what origins are allowed - that's for the server to decide.
If, for some reason, you can't stop the browser from triggering the pre-flight request, then you need to explicitly add support to your server for the OPTIONS pre-flight request. You can see how to use the cors module to do that here.
I have:
1) A client side app that has its own domain: http://client.com
2) A server side app that has a separate domain: http://server.com
Now,
the scenario is:
1) Opening http://client.com/home in the browser, which displays an HTML page.
2) http://client.com/home redirects to http://server.com/login
3) http://server.com/login stores a cookie 'auth' and sends a redirect instruction to http://client.com/welcome
Response:
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 104
Content-Type: text/html; charset=utf-8
Date: Wed, 16 Jan 2019 10:47:11 GMT
Location: http://client.com/welcome
Set-Cookie: auth=1479da80-197c-11e9-ba74-59606594e2fb; Path=/
Vary: Accept
X-Powered-By: Express
4) The browser receives the response, which does contain the cookie 'auth'
5) The browser redirects itself to http://client.com/welcome
6) 'auth' cookie is sent to http://client.com/welcome
Request:
Cookie: auth=1479da80-197c-11e9-ba74-59606594e2fb
7) http://client.com/welcome returns HTML but does not return the cookie 'auth'
8) http://client.com/welcome makes an AJAX request to http://server.com/data (CORS enabled), but the cookie 'auth' is not sent
9) http://server.com/data doesn't recognize the user because there is no cookie
The client side is an angular app hosted by Node.js
Edit:
As suggested, I've added to the response of server.com:
Access-Control-Allow-Credentials: true
but nothing has been changed.
Relevant client side code:
const headerOptions = new HttpHeaders({
'Content-Type': 'application/json', 'withCredentials': 'true', 'Access-Control-Allow-Origin': 'true', 'Access-Control-Allow-Credentials': 'true'
});
this.httpClient.get<any>(this.baseUrl + "data", { headers: headerOptions }).subscribe((res) => {
You should use the withCredentials option when sending your ajax request to your http://server.com and your server.com should have the Access-Control-Allow-Credentials set to true.
Example code in Node.JS server:
var cors = require('cors');
var corsOptions = {
origin: '*',
credentials: true };
app.use(cors(corsOptions));
More on this here: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials
Example code in Angular.JS client
import {RequestOptions, Request, RequestMethod} from '#angular/http';
const options = new RequestOptions({
method: RequestMethod.Post,
url: 'https://google.com',
withCredentials: true
});
More on this here: https://angular.io/api/http/RequestOptions
Also check this out: https://github.com/angular/angular/issues/24283 - it looks like a particular version of Angular had problems with this flag, so unless you're using a more up-to-date version, you might need to set the header explicitly.
The reasoning of this is, unless the server explicitly tells the client "I will accept cookies (set previously on my domain) passed by another domain" - accepting the cookies would be a security issue. More here: https://en.wikipedia.org/wiki/Cross-site_request_forgery
Your description of what is happening does not seem right.
http://server.com/login stores a cookie 'auth' and sends a redirect instruction to http://client.com/welcome
'auth' cookie is sent to http://client.com/welcome
That is not (or at least should not be) what is happening. When the browser requests http://server.com/login and gets back in the response a Set-Cookie header, the cookie is set on and restricted to the server.com domain, even if the response is a redirect. If you are seeing the 'auth' cookie sent to client.com then that is a cookie that client.com previously set.
Anyway, it seems that what you really care about is
http://client.com/welcome makes an AJAX request to http://server.com/data (CORS enabled), but the cookie 'auth' is not sent
There are a bunch of reasons this can happen.
CORS. You mentioned it was CORS enabled, but for the sake of others reading this, you must have the following CORS headers set on server.com
Access-Control-Allow-Origin: http://client.com
Access-Control-Allow-Credentials: true
Note that you cannot get away with using a wildcard for Access-Control-Allow-Origin when you are sending credentials. Also note that the origin has to be an exact match, including scheme (http or https). In practice, what servers generally do is read the Origin header of the request, check it against a white list, and if it allowed, copy the Origin header value from the request to the Access-Control-Allow-Origin header in the response.
You must set xhr.withCredentials = true in your XHR request. (See MDN for more details.)
Then after you have done all that, you have one other hurdle in your way. Because you are on client.com and trying to send a cookie to server.com, the server.com cookie is considered a "third-party" cookie. AFAIK all the major browsers have a setting that blocks third-party cookies for privacy, because they are most often used by trackers to gather marketing data for advertising. I believe most of them block third-party cookies by default, but I am not sure of that. For sure lots of people have set their browsers to block third-party cookies.
So you have to tell your visitors to configure their browser to allow third-party cookies from server.com.
BTW, it is not safe to set a cookie on a redirect to a different domain. While it is allowed under the specification AFAIK, there have been issues with browser support. See, for example, this Chrome bug.
From Access-Control-Allow-Origin spec:
For requests without credentials, the literal value "" can be specified, as a wildcard;*
Try to add specific domain to Access-Control-Allow-Origin field.
I think you should use proxy in angular angular app. For more info check this link: https://github.com/angular/angular-cli/blob/master/docs/documentation/stories/proxy.md
I'm using Express and Node to build a web, where the client uses RESTful API to get Json response to show a list of objects.
After creating a new object, the app should request a new json response of the updated object list. Chrome works fine - new json response returned with status code 200 OK. However, things are not good in IE and Edge - it seems the browser just fetch the json response from cache (with status code 304), instead of making a new request. If I manually clear the browser cache data things will be fine.
I tried this solution: using a middleware to set max-age of cache-control res.header to be 0
function headerSet(req, res, next) {
res.header('Cache-Control', 'public, max-age=0');
return next();
}
And in the response header I can see accordant settings taking effect, however, IE and Edge still refuse to make a new request - I'm still getting the unupdated json response with 304.
What possibly have I done wrong?
Instead of setting max age try with the following.
res.header('Cache-Control', 'private, no-cache, no-store, must-revalidate');
Firefox on my PC ended up caching my Node.js server scripts extremely hard (Ctrl+F5 ignored) after I started sending this header:
resp.writeHead(code, {
"Content-Type": mime.lookup(path),
"Content-Length": stats.size,
"Cache-Control": "public, max-age=3672000",
// Write file last modified header
"Last-Modified": stats.mtime.toUTCString()
});
The problem is that the browser caches files in back-forward (BFCache) cache instead of HTTP cache and even after about a week (during which I debugged in Google Chrome instead) still remembers them and does not revalidate. I'd like to make browser remember the Last-Modified timestamp, always send request to server and my server will decide whether to send the file or 304 - Not modified header.
Apparently this problem is somehow related to Require.js, the issue only affects javascript files loaded by that framework.