OpenGraph+ reads the HTTP cache headers from your page to figure out when to re-render social card images. If you already have caching set up, it just works.
How it works
When OpenGraph+ fetches your page, it checks the response headers:
- First request: OpenGraph+ renders your page and stores the cache headers
- Subsequent requests: If the cached image is still fresh, it’s served immediately
- Expired cache: OpenGraph+ re-fetches with conditional headers and re-renders if content changed
Cache-Control
The Cache-Control header is the main way to control caching. OpenGraph+ looks at max-age to know how long an image stays fresh.
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 |
no-cache |
Always revalidate (but can use ETag) |
no-store |
Never cache, always re-render |
Private and no-store
If your page returns Cache-Control: private or Cache-Control: no-store, OpenGraph+ re-renders on every request. Use this for pages with user-specific content that shouldn’t be cached.
ETags
ETags let you revalidate efficiently. When your page includes an ETag header, OpenGraph+ stores it and sends it back on the next request as If-None-Match.
# Your server's response
ETag: "abc123"
# OpenGraph+'s next request
If-None-Match: "abc123"
If your content hasn’t changed, your server returns 304 Not Modified and OpenGraph+ serves the cached image without re-rendering.
Last-Modified
Like ETags, the Last-Modified header enables conditional requests. OpenGraph+ sends If-Modified-Since on subsequent requests.
# Your server's response
Last-Modified: Wed, 21 Jan 2026 10:00:00 GMT
# OpenGraph+'s next request
If-Modified-Since: Wed, 21 Jan 2026 10:00:00 GMT
If your page hasn’t changed since that time, return 304 Not Modified.
Expires
The Expires header sets an absolute expiration time. OpenGraph+ uses this as a fallback when Cache-Control: max-age isn’t 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 even if the cache hasn’t expired. 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, but re-renders immediately if you change the etag to post-123-rev-6.
Priority order
OpenGraph+ uses this priority for cache settings:
- Site-level TTL (configured in your dashboard)
- Meta tags (
og:plus:cache:max_age,og:plus:cache:etag) - HTTP headers (
Cache-Control,ETag,Expires)
Default behavior
If your page returns no cache headers, OpenGraph+ defaults to a 30-day TTL. This prevents excessive re-rendering while keeping images reasonably fresh.
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 for efficient updates
- Dynamic pages: Short TTLs (1-24 hours) or
no-cachewith ETags - User-specific pages:
no-storeto prevent caching entirely