Static Serving
When Tivlo serves HTML from disk, what stays live WordPress, and the headers to look for.
Serving happens early in the WordPress request (on template_redirect).
If a matching file exists and the request looks like a public page view,
Tivlo sends the file and WordPress can stop. Generation requests
(X-Tivlo-SSG) always render live HTML so the exporter never reads a
stale file of itself. Nobody wants a photocopy of a photocopy.
Default bypass for editors
Skip static delivery for logged-in users is on by default. Log out or use a private window to see the static site. If you test next to the admin bar, you will conclude Tivlo is “off.” It is not. You are just staff.
Serve rules
A request is served from disk only when all of the following hold. If any one fails, live WordPress takes over — which is usually what you want.
- Enable static generation and Serve static HTML directly are on
- Not a generation request (
X-Tivlo-SSGabsent; generator not in-process) - Not admin, AJAX, REST, cron, or WP-CLI
- Not feed, trackback, preview, or Customizer preview
- Method is GET or HEAD (a form POST is never a snapshot)
- User is not logged in (if skip logged-in is on)
- No commerce / session cookies (see below)
- The queried post is not hard-excluded (WooCommerce cart, checkout, account, …)
- The URL is not in Never Export These Paths
- Query string is empty, or only
paged/page - Matching
index.htmlexists and is readable
Otherwise WordPress renders the page. That is a fallback, not a failure.
What stays dynamic
| Surface | Why |
|---|---|
CSS, JS, fonts, images under wp-content / wp-includes | Only the HTML document is static |
| Media in uploads | Linked from HTML; not copied unless you use S3 sync |
| Forms that POST to WordPress | POST is never served statically |
| Comment submission | Form HTML may be static; processing is not |
| Admin, login, REST, AJAX, feeds | Explicitly skipped |
| Logged-in sessions | Default skip |
| Cart / account personalization | Cookies force live PHP |
Cookie prefixes that skip static serving (filterable via
tivlo_ssg_dynamic_cookie_prefixes):
woocommerce_cart_hashwoocommerce_items_in_cartwp_woocommerce_session_edd_items_in_cartcomment_author_
If someone has items in a cart, they get a live page. Frozen “your cart is empty” would be comedy for the wrong reasons.
Query strings
Only paged and page are allowed. Any other ? parameter skips
static serve so search, filters, and tracking URLs stay dynamic. A
?utm_campaign= link is not a different page — Tivlo refuses to pretend
it is.
Response headers
| Header | Typical value | Notes |
|---|---|---|
X-Tivlo-Static | hit | Confirms a static file was sent |
X-Tivlo-Cache-Version | Cache generation token | Changes when you Invalidate Cache |
ETag | Hash of path, mtime, and cache version | Used for 304 |
Last-Modified | File mtime (GMT) | Used for 304 when ETag is absent |
Cache-Control | See below | Depends on status and invalidate window |
Content-Type | text/html; charset=… | Blog charset |
Cache-Control
| Situation | Value |
|---|---|
| Successful 200 (steady state) | public, max-age=300, stale-while-revalidate=60, must-revalidate |
| Shortly after invalidate / rebuild | no-cache, must-revalidate |
Served 404.html | no-store, must-revalidate |
304 Not Modified is returned when If-None-Match matches the ETag
(or If-Modified-Since matches, when there is no ETag mismatch and the
bust window has expired). 404 responses never collapse into 304 —
missing pages should stay missing.
404.html
If WordPress would 404, 404 Page is enabled, and
wp-content/tivlo-static/404.html is readable, Tivlo sends that
document with HTTP 404 and no-store so the missing URL is not
cached or indexed as a real page.
How is this guide?