OpenGraph+ reads the HTTP cache headers from your page to decide when to re-render social card images. If you already send cache headers, OpenGraph+ honors them.
How it works
When OpenGraph+ fetches your page, it checks the response headers:
- First request: OpenGraph+ renders your page, stores the image, and computes an expiration time from your cache headers
- While fresh: requests get the cached image immediately, without fetching your page
- After expiration: OpenGraph+ fetches the page again in full. If the new response’s ETag matches the stored one, it keeps the existing image and skips the re-render; otherwise it captures a new one
OpenGraph+ never sends conditional requests. There is no If-None-Match or If-Modified-Since, and your server will never return 304 Not Modified to it. ETags save render work, not the fetch.
Cache-Control
The Cache-Control header is the main way to control caching. OpenGraph+ reads two directives: s-maxage and max-age. When both are present, s-maxage wins, which is the standard behavior for shared caches.
Cache-Control: max-age=3600
This tells OpenGraph+ the image is good for 1 hour (3600 seconds). During that window, requests get the cached image without hitting your server.
Common values
| Header | Behavior |
|---|---|
max-age=86400 |
Fresh for 24 hours |
max-age=604800 |
Fresh for 1 week |
max-age=2592000 |
Fresh for 30 days |
s-maxage=86400, max-age=60 |
Fresh for 24 hours (s-maxage wins) |
Directives that have no effect
no-cache, no-store, and private are ignored. OpenGraph+ renders a shared image for social platforms, so there is no per-user variant to protect, and every image is cached for at least your site’s minimum cache duration (below). Don’t point OpenGraph+ at pages whose content shouldn’t be captured.
ETags
When your page includes an ETag header, OpenGraph+ stores it with the image. After the cache expires, it fetches the page again and compares the new ETag to the stored one. A match keeps the existing image and skips the expensive render; a change triggers a fresh capture.
ETag: "abc123"
Change the ETag when your content changes and OpenGraph+ picks up the new version on its next fetch.
Expires
The Expires header sets an absolute expiration time. OpenGraph+ uses it only when neither s-maxage nor max-age is present.
Expires: Wed, 22 Jan 2026 10:00:00 GMT
Prefer Cache-Control: max-age over Expires for more predictable behavior.
Meta tag overrides
If you can’t control HTTP headers, use meta tags to set cache behavior directly in your HTML. These take priority over HTTP headers.
Cache max age
Set how long the image stays fresh (in seconds):
<meta property="og:plus:cache:max_age" content="3600">
This works exactly like Cache-Control: max-age=3600.
Cache ETag
Set a version identifier for your content:
<meta property="og:plus:cache:etag" content="v1.2.3">
When the etag changes, OpenGraph+ re-renders on its next fetch. Use this to force updates when you publish new content.
Example
<head>
<meta property="og:plus:cache:max_age" content="86400">
<meta property="og:plus:cache:etag" content="post-123-rev-5">
</head>
This caches the image for 24 hours. On the fetch after that, changing the etag to post-123-rev-6 produces a new image; an unchanged etag keeps the old one without re-rendering.
Priority order
OpenGraph+ resolves the cache lifetime in this order:
- Meta tag (
og:plus:cache:max_age) - HTTP headers (
s-maxage, thenmax-age) Expiresheader (only when no max-age is set)
Whatever that produces, your site’s minimum cache duration (set in the dashboard under Settings, default 1 hour) acts as a floor: the image is cached for the longer of the two. Short header TTLs can’t push refreshes below the minimum.
Default behavior
If your page sends no cache headers at all, images are cached for your site’s minimum cache duration, which defaults to 1 hour. Raise the minimum in Settings if your pages rarely change and you want fewer renders.
Framework examples
Most frameworks make it easy to set cache headers.
Static file servers
Nginx:
nginx
location / {
expires 7d;
add_header Cache-Control "public, max-age=604800";
}
Node.js / Express
app.get('/page', (req, res) => {
res.set('Cache-Control', 'public, max-age=86400');
res.render('page');
});
PHP
header('Cache-Control: public, max-age=86400');
Python / Django
from django.views.decorators.cache import cache_control
@cache_control(max_age=86400, public=True)
def page(request):
return render(request, 'page.html')
For Rails-specific caching with ETags, see the Rails Caching guide.
Recommendations
- Static pages: Long TTLs (7-30 days) since content rarely changes
- Blog posts: Medium TTLs (1-7 days) with ETags so edits show up on the next fetch
- Dynamic pages: Short TTLs, remembering the site minimum floors them at 1 hour by default
- Freshness on demand: Change
og:plus:cache:etagwhen you publish and the next fetch re-renders