User Context
Works with:
If your application serves different content depending on the user’s group or context (guest, editor, admin), you can cache that content per user context. Each user context (group) gets its own unique hash, which is then used to vary content on. The event listener responds to hash requests and sets the Vary header. This way, you can differentiate your content between user groups while not having to store caches for each individual user.
Note
Please read the User Context chapter in the FOSHttpCache documentation before continuing.
How It Works
These five steps resemble the Overview in the FOSHttpCache documentation.
A client requests
/foo
.The proxy server receives the request and holds it. It first sends a hash request to the context hash route.
The application receives the hash request. An event listener (
UserContextListener
) aborts the request immediately after the Symfony firewall was applied. The application calculates the hash (HashGenerator
) and then sends a response with the hash in a custom header (X-User-Context-Hash
by default).The caching proxy receives the hash response, copies the hash header to the client’s original request for
/foo
and restarts that request.If the response to
/foo
should differ per user context, the application sets aVary: X-User-Context-Hash
header. The appropriate user context dependent representation of/foo
will then be returned to the client.
Note
In other words we execute a preflight request here. A preflight request is
a request that is sent prior to the real request. See for example how
CORS requests work. The result of the preflight request is the
X-User-Context-Hash
header that is added to the real request.
This concept can be generalized to handle more than the user context
scenario. The terminal42/header-replay-bundle builds on top of the
FOSHttpCacheBundle
to add support for more complicated use cases. Have
a look at the HeaderReplayBundle documentation if the user context is
not flexible enough for your needs.
Configuration
First configure your caching proxy. Then configure Symfony for handling hash lookups. The minimal steps are described below, see the reference for more details.
You need to configure a route for the context hash. It does not specify any controller, as the request listener will abort the request right after the firewall has been applied, but the route definition must exist. Use the same path as you specified in the caching proxy and make sure that this path is allowed for anonymous users and covered by your firewall configuration:
# app/config/routing.yml
user_context_hash:
path: /_fos_user_context_hash
If your access rules limit the whole site to logged in users, make sure to handle the user context URL like the login page:
# app/config/security.yml
access_control:
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/_fos_user_context_hash, roles: [IS_AUTHENTICATED_ANONYMOUSLY] }
- { path: ^/, roles: ROLE_USER }
Finally, enable the listener with the default settings:
# app/config/config.yml
fos_http_cache:
user_context:
enabled: true
Warning
If your site is using flash messages to display information to users after redirects, you need to configure the flash message listener to avoid mixing up messages between your users.
Note
When using the FOSRestBundle format_listener
configuration on all paths
of your site, the hash lookup will fail with “406 Not Acceptable - No
matching accepted Response format could be determined”. To avoid this
problem, you need to add a rule to the format listener configuration:
- { path: '^/_fos_user_context_hash', stop: true }
Generating Hashes
When a context hash request is received, the HashGenerator
is used to build
the context information. The generator does so by calling on one or more
context providers.
The bundle includes a simple role_provider
that determines the hash from the
user’s roles. To enable it:
# app/config/config.yml
fos_http_cache:
user_context:
role_provider: true
Alternatively, you can create a custom context provider.
Caching Hash Responses
To improve User Context Caching performance, you should cache the hash responses. You can do so by configuring hash_cache_ttl.