diff --git a/README.md b/README.md index bd6ebbc1..0d19d79f 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ var corsOptions = { optionsSuccessStatus: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204 } -// Adds headers: Access-Control-Allow-Origin: http://example.com, Vary: Origin +// Adds headers: Access-Control-Allow-Origin: http://example.com app.get('/products/:id', cors(corsOptions), function (req, res, next) { res.json({msg: 'Hello'}) }) @@ -189,7 +189,7 @@ Here’s an example that handles both public routes and restricted, credential-s var dynamicCorsOptions = function(req, callback) { var corsOptions; if (req.path.startsWith('/auth/connect/')) { - // Access-Control-Allow-Origin: http://mydomain.com, Access-Control-Allow-Credentials: true, Vary: Origin + // Access-Control-Allow-Origin: http://mydomain.com, Access-Control-Allow-Credentials: true corsOptions = { origin: 'http://mydomain.com', credentials: true @@ -226,6 +226,8 @@ app.listen(80, function () { - `RegExp` - set `origin` to a regular expression pattern which will be used to test the request origin. If it's a match, the request origin will be reflected. For example the pattern `/example\.com$/` will reflect any request that is coming from an origin ending with "example.com". - `Array` - set `origin` to an array of valid origins. Each origin can be a `String` or a `RegExp`. For example `["http://example1.com", /\.example2\.com$/]` will accept any request from "http://example1.com" or from a subdomain of "example2.com". - `Function` - set `origin` to a function implementing some custom logic. The function takes the request origin as the first parameter and a callback (called as `callback(err, origin)`, where `origin` is a non-function value of the `origin` option) as the second. + + `Vary: Origin` is added when the allowed origin is reflected from the request origin, such as with `origin: true`, a `RegExp`, an `Array`, or an origin callback. It is not added for `origin: "*"`, `origin: false`, or a static string origin. * `methods`: Configures the **Access-Control-Allow-Methods** CORS header. Expects a comma-delimited string (ex: 'GET,PUT,POST') or an array (ex: `['GET', 'PUT', 'POST']`). * `allowedHeaders`: Configures the **Access-Control-Allow-Headers** CORS header. Expects a comma-delimited string (ex: 'Content-Type,Authorization') or an array (ex: `['Content-Type', 'Authorization']`). If not specified, defaults to reflecting the headers specified in the request's **Access-Control-Request-Headers** header. * `exposedHeaders`: Configures the **Access-Control-Expose-Headers** CORS header. Expects a comma-delimited string (ex: 'Content-Range,X-Content-Range') or an array (ex: `['Content-Range', 'X-Content-Range']`). If not specified, no custom headers are exposed. diff --git a/lib/index.js b/lib/index.js index ad899cae..46c260a6 100644 --- a/lib/index.js +++ b/lib/index.js @@ -50,10 +50,12 @@ key: 'Access-Control-Allow-Origin', value: options.origin }]); - headers.push([{ - key: 'Vary', - value: 'Origin' - }]); + if (options._originFromCallback) { + headers.push([{ + key: 'Vary', + value: 'Origin' + }]); + } } else { isAllowed = isOriginAllowed(requestOrigin, options.origin); // reflect origin @@ -216,11 +218,13 @@ } if (originCallback) { + var originFromCallback = typeof corsOptions.origin === 'function'; originCallback(req.headers.origin, function (err2, origin) { if (err2 || !origin) { next(err2); } else { corsOptions.origin = origin; + corsOptions._originFromCallback = originFromCallback; cors(corsOptions, req, res, next); } }); diff --git a/test/test.js b/test/test.js index 34ddb41b..4cc0661f 100644 --- a/test/test.js +++ b/test/test.js @@ -258,7 +258,7 @@ var util = require('util') cors(options)(req, res, next); }); - it('includes Vary header for specific origins', function (done) { + it('does not include Vary header for static origins', function (done) { // arrange var req, res, next, options; options = { @@ -268,7 +268,7 @@ var util = require('util') res = fakeResponse(); next = function () { // assert - assert.equal(res.getHeader('Vary'), 'Origin') + assert.equal(res.getHeader('Vary'), undefined) done(); }; @@ -276,7 +276,7 @@ var util = require('util') cors(options)(req, res, next); }); - it('appends to an existing Vary header', function (done) { + it('does not append Origin to an existing Vary header for static origins', function (done) { // arrange var req, res, next, options; options = { @@ -287,7 +287,7 @@ var util = require('util') res.setHeader('Vary', 'Foo'); next = function () { // assert - assert.equal(res.getHeader('Vary'), 'Foo, Origin') + assert.equal(res.getHeader('Vary'), 'Foo') done(); }; @@ -345,6 +345,24 @@ var util = require('util') cors(options)(req, res, next); }); + it('includes Vary header when origin callback returns a specific origin', function (done) { + var req, res, next, options; + options = { + origin: function (sentOrigin, cb) { + cb(null, sentOrigin); + } + }; + req = fakeRequest('GET'); + res = fakeResponse(); + next = function () { + assert.equal(res.getHeader('Access-Control-Allow-Origin'), 'http://example.com') + assert.equal(res.getHeader('Vary'), 'Origin') + done(); + }; + + cors(options)(req, res, next); + }); + it('should not allow origin when callback returns false', function (done) { var req, res, next, options; options = {