Chapter 6

HTTP Caching

Control when images are refreshed

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:

  1. First request: OpenGraph+ renders your page and stores the cache headers
  2. Subsequent requests: If the cached image is still fresh, it’s served immediately
  3. 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:

  1. Site-level TTL (configured in your dashboard)
  2. Meta tags (og:plus:cache:max_age, og:plus:cache:etag)
  3. 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-cache with ETags
  • User-specific pages: no-store to prevent caching entirely