mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Migrate documentation from 3.x
This commit is contained in:
parent
6f32762268
commit
3ea98cdb13
@ -76,6 +76,17 @@ functionality is available as an additional [Spam Protection](https://github.com
|
||||
module if required. The module provides an consistent API for allowing third-party spam protection handlers such as
|
||||
[Recaptcha](http://www.google.com/recaptcha/intro/) and [Mollom](https://mollom.com/) to work within the `Form` API.
|
||||
|
||||
## Data disclosure through HTTP Caching (since 4.2.0)
|
||||
|
||||
Forms, and particularly their responses, can contain sensitive data (such as when data is pre-populated or re-posted due
|
||||
to validation errors). This data can inadvertently be stored either in a user's browser cache or in an intermediary
|
||||
cache such as a CDN or other caching-proxy. If incorrect `Cache-Conrol` headers are used, private data may be cached and
|
||||
accessible publicly through the CDN.
|
||||
|
||||
To ensure this doesn't happen SilverStripe adds `Cache-Control: no-store, no-cache, must-revalidate` headers to any
|
||||
forms that have validators or security tokens (all of them by default) applied to them; this ensures that CDNs
|
||||
(and browsers) will not cache these pages.
|
||||
|
||||
## Related Documentation
|
||||
|
||||
* [Security](../security)
|
||||
|
@ -1,7 +1,188 @@
|
||||
title: HTTP Cache Headers
|
||||
summary: Set the correct HTTP cache headers for your responses.
|
||||
|
||||
# Caching Headers
|
||||
# HTTP Cache Headers
|
||||
|
||||
## Overview
|
||||
|
||||
By default, SilverStripe sends headers which signal to HTTP caches
|
||||
that the response should be not considered cacheable.
|
||||
HTTP caches can either be intermediary caches (e.g. CDNs and proxies), or clients (e.g. browsers).
|
||||
The cache headers sent are `Cache-Control: no-store, no-cache, must-revalidate`;
|
||||
|
||||
HTTP caching can be a great way to speed up your website, but needs to be properly applied.
|
||||
Getting it wrong can accidentally expose draft pages or other protected content.
|
||||
The [Google Web Fundamentals](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching#public_vs_private)
|
||||
are a great way to learn about HTTP caching.
|
||||
|
||||
## Cache Control Headers
|
||||
|
||||
### Overview
|
||||
|
||||
In order to support developers in making safe choices around HTTP caching,
|
||||
we're using a `HTTPCacheControl` class to control if a response
|
||||
should be considered public or private. This is an abstraction on existing
|
||||
lowlevel APIs like `HTTP::add_cache_headers()` and `SS_HTTPResponse->addHeader()`.
|
||||
|
||||
The `HTTPCacheControl` API makes it easier to express your caching preferences
|
||||
without running the risk of overriding essential core safety measures.
|
||||
Most commonly, these APIs will prevent HTTP caching of draft content.
|
||||
|
||||
It will also prevent caching of content generated with an active session,
|
||||
since the system can't tell whether session data was used to vary the output.
|
||||
In this case, it's up to the developer to opt-in to caching,
|
||||
after ensuring that certain execution paths are safe despite of using sessions.
|
||||
|
||||
The system behaviour does not guard against accidentally caching "private" content,
|
||||
since there are too many variations under which output could be considered private
|
||||
(e.g. a custom "approval" flag on a comment object). It is up to
|
||||
the developer to ensure caching is used appropriately there.
|
||||
|
||||
The [api:SilverStripe\Control\Middleware\HTTPCacheControlMiddleware] class supplements the `HTTP` helper class.
|
||||
It comes with methods which let developers safely interact with the `Cache-Control` header.
|
||||
|
||||
### disableCache()
|
||||
|
||||
Simple way to set cache control header to a non-cacheable state.
|
||||
Use this method over `privateCache()` if you are unsure about caching details.
|
||||
Takes precendence over unforced `enableCache()`, `privateCache()` or `publicCache()` calls.
|
||||
|
||||
Removes all state and replaces it with `no-cache, no-store, must-revalidate`. Although `no-store` is sufficient
|
||||
the others are added under [recommendation from Mozilla](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#Examples)
|
||||
|
||||
Does not set `private` directive, use `privateCache()` if this is explicitly required
|
||||
([details](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching#public_vs_private))
|
||||
|
||||
### enableCache()
|
||||
|
||||
Simple way to set cache control header to a cacheable state.
|
||||
Use this method over `publicCache()` if you are unsure about caching details.
|
||||
|
||||
Removes `no-store` and `no-cache` directives; other directives will remain in place.
|
||||
Use alongside `setMaxAge()` to indicate caching.
|
||||
|
||||
Does not set `public` directive. Usually, `setMaxAge()` is sufficient. Use `publicCache()` if this is explicitly required
|
||||
([details](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching#public_vs_private))
|
||||
|
||||
### privateCache()
|
||||
|
||||
Advanced way to set cache control header to a non-cacheable state.
|
||||
Indicates that the response is intended for a single user and must not be stored by a shared cache.
|
||||
A private cache (e.g. Web Browser) may store the response. Also removes `public` as this is a contradictory directive.
|
||||
|
||||
### publicCache()
|
||||
|
||||
Advanced way to set cache control header to a cacheable state.
|
||||
Indicates that the response may be cached by any cache. (eg: CDNs, Proxies, Web browsers)
|
||||
Also removes `private` as this is a contradictory directive
|
||||
|
||||
### Priority
|
||||
|
||||
Each of these highlevel methods has a boolean `$force` parameter which determines
|
||||
their application priority regardless of execution order.
|
||||
The priority order is as followed, sorted in descending order
|
||||
(earlier items will overrule later items):
|
||||
|
||||
* `disableCache($force=true)`
|
||||
* `privateCache($force=true)`
|
||||
* `publicCache($force=true)`
|
||||
* `enableCache($force=true)`
|
||||
* `disableCache()`
|
||||
* `privateCache()`
|
||||
* `publicCache()`
|
||||
* `enableCache()`
|
||||
|
||||
## Cache Control Examples
|
||||
|
||||
### Global opt-in for page content
|
||||
|
||||
Enable caching for all page content (through `Page_Controller`).
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use SilverStripe\Control\Middleware\HTTPCacheControlMiddleware;
|
||||
use SilverStripe\CMS\Controllers\ContentController;
|
||||
|
||||
class PageController extends ContentController
|
||||
{
|
||||
public function init()
|
||||
{
|
||||
HTTPCacheControlMiddleware::singleton()
|
||||
->enableCache()
|
||||
->setMaxAge(60); // 1 minute
|
||||
|
||||
parent::init();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note: SilverStripe will still override this preference when a session is active,
|
||||
a [CSRF token](/developer_guides/forms/form_security) token is present,
|
||||
or draft content has been requested.
|
||||
|
||||
### Opt-out for a particular controller action
|
||||
|
||||
If a controller output relies on session data, cookies,
|
||||
permission checks or other triggers for conditional output,
|
||||
you can disable caching either on a controller level
|
||||
(through `init()`) or for a particular action.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use SilverStripe\Control\Middleware\HTTPCacheControlMiddleware;
|
||||
use SilverStripe\CMS\Controllers\ContentController;
|
||||
|
||||
class PageController extends ContentController
|
||||
{
|
||||
public function myprivateaction($request)
|
||||
{
|
||||
HTTPCacheControlMiddleware::singleton()
|
||||
->disableCache();
|
||||
|
||||
return $this->myPrivateResponse();;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note: SilverStripe will still override this preference when a session is active,
|
||||
a [CSRF token](/developer_guides/forms/form_security) token is present,
|
||||
or draft content has been requested.
|
||||
|
||||
### Global opt-in, ignoring session (advanced)
|
||||
|
||||
This can be helpful in situations where forms are embedded on the website.
|
||||
SilverStripe will still override this preference when draft content has been requested.
|
||||
CAUTION: This mode relies on a developer examining each execution path to ensure
|
||||
that no session data is used to vary output.
|
||||
|
||||
Use case: By default, forms include a [CSRF token](/developer_guides/forms/form_security)
|
||||
which starts a session with a value that's unique to the visitor, which makes the output uncacheable.
|
||||
But any subsequent requests by this visitor will also carry a session, leading to uncacheable output
|
||||
for this visitor. This is the case even if the output does not contain any forms,
|
||||
and does not vary for this particular visitor.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use SilverStripe\Control\Middleware\HTTPCacheControlMiddleware;
|
||||
use SilverStripe\CMS\Controllers\ContentController;
|
||||
|
||||
class PageController extends ContentController
|
||||
{
|
||||
public function init()
|
||||
{
|
||||
HTTPCacheControlMiddleware::inst()
|
||||
->enableCache($force=true) // DANGER ZONE
|
||||
->setMaxAge(60); // 1 minute
|
||||
|
||||
parent::init();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Defaults
|
||||
|
||||
By default, PHP adds caching headers that make the page appear purely dynamic. This isn't usually appropriate for most
|
||||
sites, even ones that are updated reasonably frequently. SilverStripe overrides the default settings with the following
|
||||
@ -14,39 +195,41 @@ headers:
|
||||
* Since a visitor cookie is set, the site won't be cached by proxies.
|
||||
* Ajax requests are never cached.
|
||||
|
||||
## Customizing Cache Headers
|
||||
## Max Age
|
||||
|
||||
### HTTP::set_cache_age
|
||||
The cache age determines the lifetime of your cache, in seconds.
|
||||
It only takes effect if you instruct the cache control
|
||||
that your response is public in the first place (via `enableCache()` or via modifying the `HTTP.cache_control` defaults).
|
||||
|
||||
```php
|
||||
use SilverStripe\Control\Middleware\HTTPCacheControlMiddleware;
|
||||
HTTPCacheControlMiddleware::singleton()
|
||||
->setMaxAge(60)
|
||||
```
|
||||
|
||||
Note that `setMaxAge(0)` is NOT sufficient to disable caching in all cases.
|
||||
|
||||
### Last Modified
|
||||
|
||||
Used to set the modification date to something more recent than the default. [api:DataObject::__construct] calls
|
||||
[api:HTTP::register_modification_date(] whenever a record comes from the database ensuring the newest date is present.
|
||||
|
||||
```php
|
||||
use SilverStripe\Control\HTTP;
|
||||
|
||||
HTTP::set_cache_age(0);
|
||||
```
|
||||
|
||||
Used to set the max-age component of the cache-control line, in seconds. Set it to 0 to disable caching; the "no-cache"
|
||||
clause in `Cache-Control` and `Pragma` will be included.
|
||||
|
||||
### HTTP::register_modification_date
|
||||
|
||||
|
||||
```php
|
||||
HTTP::register_modification_date('2014-10-10');
|
||||
```
|
||||
|
||||
Used to set the modification date to something more recent than the default. [DataObject::__construct](api:SilverStripe\ORM\DataObject::__construct) calls
|
||||
[HTTP::register_modification_date(](api:SilverStripe\Control\HTTP::register_modification_date() whenever a record comes from the database ensuring the newest date is present.
|
||||
### Vary
|
||||
|
||||
### Vary: cache header
|
||||
|
||||
By default, SilverStripe will output a `Vary` header (used by upstream caches for determining uniqueness)
|
||||
that looks like
|
||||
A `Vary` header tells caches which aspects of the response should be considered
|
||||
when calculating a cache key, usually in addition to the full URL path.
|
||||
By default, SilverStripe will output a `Vary` header with the following content:
|
||||
|
||||
```
|
||||
Cookie, X-Forwarded-Protocol, User-Agent, Accept
|
||||
Vary: X-Requested-With, X-Forwarded-Protocol
|
||||
```
|
||||
|
||||
To change the value of the `Vary` header, you can change this value by specifying the header in configuration
|
||||
To change the value of the `Vary` header, you can change this value by specifying the header in configuration.
|
||||
|
||||
```yml
|
||||
SilverStripe\Control\HTTP:
|
||||
|
@ -770,6 +770,15 @@ class MySecureController extends Controller
|
||||
}
|
||||
```
|
||||
|
||||
## HTTP Caching Headers
|
||||
|
||||
Caching is hard. If you get it wrong, private or draft content might leak
|
||||
to unauthenticated users. We have created an abstraction which allows you to express
|
||||
your intent around HTTP caching without worrying too much about the details.
|
||||
See [/developer_guides/performances/http_cache_headers](Developer Guides > Performance > HTTP Cache Headers)
|
||||
for details on how to apply caching safely, and read Google's
|
||||
[Web Fundamentals on Caching](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching).
|
||||
|
||||
## Related
|
||||
|
||||
* [http://silverstripe.org/security-releases/](http://silverstripe.org/security-releases/)
|
||||
|
@ -201,3 +201,147 @@ SilverStripe\Core\Injector\Injector:
|
||||
args:
|
||||
disable-container: true
|
||||
```
|
||||
|
||||
### HTTP Cache Header changes
|
||||
|
||||
#### Overview
|
||||
|
||||
In order to support developers in making safe choices around HTTP caching,
|
||||
we've introduced a `HTTPCacheControlMiddleware` class to control if a response
|
||||
should be considered public or private. This is an abstraction on existing
|
||||
lowlevel APIs like `HTTP::add_cache_headers()` and `SS_HTTPResponse->addHeader()`.
|
||||
|
||||
This change introduces smaller but necessary changes to HTTP caching headers
|
||||
sent by SilverStripe. If you are relying on HTTP caching in your implementation,
|
||||
or use modules such as [silverstripe/controllerpolicy](https://github.com/silverstripe/silverstripe-controllerpolicy),
|
||||
please review the implications of these changes below.
|
||||
|
||||
In short, these APIs make it easier to express your caching preferences
|
||||
without running the risk of overriding essential core safety measures.
|
||||
Most commonly, these APIs will prevent HTTP caching of draft content.
|
||||
|
||||
It will also prevent caching of content generated with an active session,
|
||||
since the system can't tell whether session data was used to vary the output.
|
||||
In this case, it's up to the developer to opt-in to caching,
|
||||
after ensuring that certain execution paths are safe despite of using sessions.
|
||||
|
||||
The system behaviour does not guard against accidentally caching "private" content,
|
||||
since there are too many variations under which output could be considered private
|
||||
(e.g. a custom "approval" flag on a comment object). It is up to
|
||||
the developer to ensure caching is used appropriately there.
|
||||
|
||||
By default, SilverStripe sends headers which signal to HTTP caches
|
||||
that the response should be considered not cacheable.
|
||||
|
||||
See [Developer Guide: Performance > HTTP Cache Headers](/developer_guide/performance/http_cache_headers)
|
||||
for details on the new API.
|
||||
|
||||
#### Example Usage
|
||||
|
||||
##### Global opt-in for page content
|
||||
|
||||
Enable caching for all page content (through `Page_Controller`).
|
||||
|
||||
```diff
|
||||
<?php
|
||||
|
||||
-use SilverStripe\Control\HTTP;
|
||||
+use SilverStripe\Control\Middleware\HTTPCacheControlMiddleware;
|
||||
use SilverStripe\CMS\Controllers\ContentController;
|
||||
|
||||
class PageController extends ContentController
|
||||
{
|
||||
public function init()
|
||||
{
|
||||
- HTTP::set_cache_age(60);
|
||||
+ HTTPCacheControlMiddleware::inst()
|
||||
+ ->enableCache()
|
||||
+ ->setMaxAge(60); // 1 minute
|
||||
|
||||
parent::init();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note: SilverStripe will still override this preference when a session is active,
|
||||
a [CSRF token](/developer_guides/forms/form_security) token is present,
|
||||
or draft content has been requested.
|
||||
|
||||
##### Opt-out for a particular controller action
|
||||
|
||||
If a controller output relies on session data, cookies,
|
||||
permission checks or other triggers for conditional output,
|
||||
you can disable caching either on a controller level
|
||||
(through `init()`) or for a particular action.
|
||||
|
||||
```diff
|
||||
<?php
|
||||
|
||||
-use SilverStripe\Control\HTTP;
|
||||
+use SilverStripe\Control\Middleware\HTTPCacheControlMiddleware;
|
||||
use SilverStripe\CMS\Controllers\ContentController;
|
||||
|
||||
class PageController extends ContentController
|
||||
{
|
||||
public function myprivateaction($request)
|
||||
{
|
||||
- HTTP::set_cache_age(0);
|
||||
+ HTTPCacheControl::inst()
|
||||
+ ->disableCache();
|
||||
|
||||
return $this->myPrivateResponse();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note: SilverStripe will still override this preference when a session is active,
|
||||
a [CSRF token](/developer_guides/forms/form_security) token is present,
|
||||
or draft content has been requested.
|
||||
|
||||
##### Global opt-in, ignoring session (advanced)
|
||||
|
||||
This can be helpful in situations where forms are embedded on the website.
|
||||
SilverStripe will still override this preference when draft content has been requested.
|
||||
CAUTION: This mode relies on a developer examining each execution path to ensure
|
||||
that no session data is used to vary output.
|
||||
|
||||
Use case: By default, forms include a [CSRF token](/developer_guides/forms/form_security)
|
||||
which starts a session with a value that's unique to the visitor, which makes the output uncacheable.
|
||||
But any subsequent requests by this visitor will also carry a session, leading to uncacheable output
|
||||
for this visitor. This is the case even if the output does not contain any forms,
|
||||
and does not vary for this particular visitor.
|
||||
|
||||
```diff
|
||||
<?php
|
||||
|
||||
-use SilverStripe\Control\HTTP;
|
||||
+use SilverStripe\Control\Middleware\HTTPCacheControlMiddleware;
|
||||
use SilverStripe\CMS\Controllers\ContentController;
|
||||
|
||||
class PageController extends ContentController
|
||||
{
|
||||
public function init()
|
||||
{
|
||||
- HTTP::set_cache_age(60);
|
||||
+ HTTPCacheControl::inst()
|
||||
+ ->enableCache($force=true) // DANGER ZONE
|
||||
+ ->setMaxAge(60); // 1 minute
|
||||
|
||||
parent::init();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Detailed Cache-Control Changes:
|
||||
|
||||
* Added `Cache-Control: no-store` header to default responses,
|
||||
to prevent intermediary HTTP proxies (e.g. CDNs) from caching unless developers opt-in
|
||||
* Removed `Cache-Control: no-transform` header from default responses
|
||||
* Removed `Vary: Cookie` as an unreliable cache buster,
|
||||
rely on the existing `Cache-Control: no-store` defaults instead
|
||||
* Removed `Vary: Accept`, since it's very uncommon to vary content on
|
||||
the `Accept` headers submitted through the request,
|
||||
and it can significantly decrease the likelyhood of a cache hit.
|
||||
Note this is different from `Vary: Accept-Encoding`,
|
||||
which is important for compression (e.g. gzip), and usually added by
|
||||
other layers such as Apache's mod_gzip.
|
||||
|
Loading…
Reference in New Issue
Block a user