diff --git a/README.md b/README.md index c42d4a7..c1bf5e0 100644 --- a/README.md +++ b/README.md @@ -204,6 +204,7 @@ app.listen(80, function () { * `maxAge`: Configures the **Access-Control-Max-Age** CORS header. Set to an integer to pass the header, otherwise it is omitted. * `preflightContinue`: Pass the CORS preflight response to the next handler. * `optionsSuccessStatus`: Provides a status code to use for successful `OPTIONS` requests, since some legacy browsers (IE11, various SmartTVs) choke on `204`. +* `shouldSetVaryOriginHeader`: takes a `req`, and determines whether the `Vary: Origin` header should be set for that header name. Does nothing for pre-flight OPTIONS requests. As an example, `shouldSetVaryOriginHeader(req) { !req.originalUrl.startsWith('/images')) }` would set the Vary header to `Vary: Origin` for all request URIs besides those that start with `/images`; for requests that start with `/images`, it would not set the `Vary` header. This is mostly useful for working with caching and edge services that do not support the `Vary` header, such as [Cloudflare Polish](https://developers.cloudflare.com/images/polish/compression/); it is also useful for working with CDNs in general, because the `Vary` header can cause cache fragmentation and lower cache hit ratios depending on how it is configured. The default configuration is the equivalent of: diff --git a/lib/index.js b/lib/index.js index ad899ca..c4ab7c7 100644 --- a/lib/index.js +++ b/lib/index.js @@ -9,7 +9,8 @@ origin: '*', methods: 'GET,HEAD,PUT,PATCH,POST,DELETE', preflightContinue: false, - optionsSuccessStatus: 204 + optionsSuccessStatus: 204, + shouldSetVaryOriginHeader: function(req) { return true } }; function isString(s) { @@ -50,10 +51,12 @@ key: 'Access-Control-Allow-Origin', value: options.origin }]); - headers.push([{ - key: 'Vary', - value: 'Origin' - }]); + if (options.shouldSetVaryOriginHeader(req)) { + headers.push([{ + key: 'Vary', + value: 'Origin' + }]); + } } else { isAllowed = isOriginAllowed(requestOrigin, options.origin); // reflect origin @@ -61,10 +64,12 @@ key: 'Access-Control-Allow-Origin', value: isAllowed ? requestOrigin : false }]); - headers.push([{ - key: 'Vary', - value: 'Origin' - }]); + if (options.shouldSetVaryOriginHeader(req)) { + headers.push([{ + key: 'Vary', + value: 'Origin' + }]); + } } return headers; diff --git a/test/test.js b/test/test.js index f2a2e94..9180877 100644 --- a/test/test.js +++ b/test/test.js @@ -150,6 +150,97 @@ var util = require('util') }) }); + describe('shouldSetVaryOriginHeader', function() { + it('Vary: "Origin" header is set by default', function (done) { + var req = fakeRequest('GET') + var res = fakeResponse() + req.host = 'http://example.com' + req.originalUrl = '/images/asdf.png' + var options = { + origin: 'http://example.com' + } + + var next = function () { + // assert + assert.equal(res.statusCode, 200) + assert.equal(res.getHeader('Vary'), 'Origin') + done(); + }; + + cors(options)(req, res, next) + }) + + it('Vary: "Origin" header is not set when shouldSetVaryOriginHeader(req) configured to return false', function (done) { + var req = fakeRequest('GET') + var res = fakeResponse() + req.host = 'http://example.com' + req.originalUrl = '/images/asdf.png' + + var options = { + origin: ['http://example.com', 'http://foo.com'], + shouldSetVaryOriginHeader: function(req) { + return req.originalUrl.indexOf('/images') !== 0 + } + } + + var next = function () { + // assert + assert.equal(res.statusCode, 200) + assert.equal(res.getHeader('Vary'), undefined) + done(); + }; + + cors(options)(req, res, next) + }) + + it('Vary: "Origin" header is not set when shouldSetVaryOriginHeader(req) configured to return false and origin is string', function (done) { + var req = fakeRequest('GET') + var res = fakeResponse() + req.host = 'http://example.com' + req.originalUrl = '/images/asdf.png' + + var options = { + origin: 'http://example.com', + shouldSetVaryOriginHeader: function(req) { + return req.originalUrl.indexOf('/images') !== 0 + } + } + + var next = function () { + // assert + assert.equal(res.statusCode, 200) + assert.equal(res.getHeader('Vary'), undefined) + done(); + }; + + cors(options)(req, res, next) + }) + + it('Vary: "Origin" header is set when shouldSetVaryOriginHeader(req) configured to return true', function (done) { + var req = fakeRequest('GET') + var res = fakeResponse() + req.host = 'http://example.com' + + req.originalUrl = '/some/other/path' + + var options = { + origin: ['http://example.com', 'http://foo.com'], + shouldSetVaryOriginHeader: function(req) { + return req.originalUrl.indexOf('/images') !== 0 + } + } + + var next = function () { + // assert + assert.equal(res.statusCode, 200) + assert.equal(res.getHeader('Vary'), 'Origin') + done(); + }; + + cors(options)(req, res, next) + }) + }) + describe('passing static options', function () { it('overrides defaults', function (done) { var cb = after(1, done)