OpenGraph+ reads HTTP cache headers from your Rails responses to decide when to re-render social card images. Rails has built-in support for ETags and cache headers, so this is pretty straightforward.
ETags with fresh_when
The fresh_when helper sets ETag and Last-Modified headers based on your record. OpenGraph+ uses these for conditional requests. If your content hasn’t changed, your server returns 304 Not Modified and OpenGraph+ serves the cached image.
class PostsController < ApplicationController
def show
@post = Post.find(params[:id])
fresh_when @post
end
end
This sets both ETag (based on the record’s cache key) and Last-Modified (based on updated_at).
Collection ETags
For index pages, pass a collection:
def index
@posts = Post.published.order(created_at: :desc)
fresh_when @posts
end
Rails generates an ETag from the entire collection, so any change to any post invalidates the cache.
Custom ETags
Build your own ETag from multiple values:
def show
@post = Post.find(params[:id])
fresh_when etag: [@post, current_user&.id, @post.comments.maximum(:updated_at)]
end
Cache-Control with expires_in
Set explicit TTLs with expires_in:
class PostsController < ApplicationController
def show
@post = Post.find(params[:id])
expires_in 1.day, public: true
end
end
This sets Cache-Control: public, max-age=86400. OpenGraph+ serves the cached image for 24 hours without hitting your server.
Common patterns
# Static pages - cache for a week
expires_in 1.week, public: true
# Blog posts - cache for a day
expires_in 1.day, public: true
# Dynamic content - cache for an hour
expires_in 1.hour, public: true
# User-specific content - don't cache
expires_in 0, public: false
Combining ETags and TTLs
Use both for fast responses during the TTL and efficient revalidation after.
def show
@post = Post.find(params[:id])
expires_in 1.hour, public: true
fresh_when @post
end
During the first hour, OpenGraph+ serves the cached image immediately. After that, it revalidates with the ETag. If the post hasn’t changed, your server returns 304 and OpenGraph+ keeps serving the cached image.
stale? for conditional rendering
Use stale? when you only want to do work if the cache is invalid:
def show
@post = Post.find(params[:id])
if stale?(@post)
# Only runs if the ETag doesn't match
@related_posts = @post.related_posts
@comments = @post.comments.recent
end
end
When OpenGraph+ sends a conditional request and the ETag matches, Rails returns 304 without executing the block.
Website-level TTL override
OpenGraph+ also has a website-level cache TTL setting in your dashboard. When set, it overrides HTTP headers entirely. This is handy when origin servers have bad or missing cache headers.
If you control your Rails app, stick with HTTP headers for caching. The website TTL override is a fallback for sites you don’t control.
Recommendations
| Page type | Strategy |
|---|---|
| Static pages | expires_in 1.week, public: true |
| Blog posts | expires_in 1.day + fresh_when @post |
| Index pages | expires_in 1.hour + fresh_when @posts |
| User dashboards | expires_in 0 or no caching |