SelloraDocumentation v1.1.0
Docs / Store configuration

Performance and caching

Full-page cache, fragment caches, OPcache, optimize, Redis, queues and server headers. What is cached, when it refreshes, and what to do when a change does not show.

Sellora is built to answer most storefront requests from a ready-made copy of the page. On ordinary shared hosting with the file cache, a cached page is answered in a few milliseconds, without starting the application or querying the database. Open Settings > Performance to see the status and to change the options.

What is cached#

Layer What Who gets it Refreshed
Full-page cache Complete HTML of the home page, shop, categories, brands, tags, product pages, search results, blog, CMS pages, About, FAQ, Contact, the quick view and the product reviews pages Visitors who are not signed in, have an empty cart, wishlist and compare list and see no message (for example "Saved" or form errors) On every change in the admin, and after the page lifetime (default 10 minutes)
Early hits The same cached pages, answered from public/index.php before Laravel starts First-time visitors and crawlers without a language or currency cookie (file cache only) Same as above
Fragment caches Home sections (rendered HTML), menus, category tree, sliders, flash sale, currencies, languages, settings, related products and "more from this brand" (product id lists), review statistics Everybody, including signed-in customers On every change in the admin (shared version key) and after one hour
Browser cache Cached pages are sent with ETag and Last-Modified; browsers check back each time and get a tiny "304 Not Modified" when nothing changed Everybody Every visit (checked)

Never cached: the cart, checkout, account pages, wishlist and compare pages, sign-in and sign-up pages, order tracking, the admin, the installer, the REST API and payment pages. These pages are sent with Cache-Control: private, no-store.

Every storefront response carries the header X-Sellora-Cache: HIT, MISS (built now and stored) or BYPASS (built for this visitor only). Early hits also send X-Sellora-Cache-Layer: early.

Personal parts of a cached page#

A cached page is the same for every visitor who receives it, so it contains nothing personal:

  • The CSRF security token is left empty in the stored copy. The storefront script loads a fresh token from GET /state as soon as the page opens, and any form sent before that waits for it. Add to cart, newsletter, reviews and every other form work as usual.
  • GET /state also records what the full page would have recorded: the visit for Reports > Analytics, the product view and the "Recently viewed" list, blog post views and the search term for Reports > Search terms.
  • As soon as a visitor signs in, adds something to the cart, wishlist or compare list, or sees a message, the browser receives a small sl_nc cookie and gets freshly built pages from then on. Visitors with a "Recently viewed" list (sl_rv cookie) get freshly built product pages.

Language, currency and demo#

The cache key contains the address, the query string (tracking parameters such as utm_source, gclid and fbclid are ignored), the language, the currency and, in the live demo, the demo store. A visitor who switches to Euro or Arabic gets the Euro or Arabic copy; the first visitor in each combination builds it.

When the cache refreshes#

You do not need to clear anything after normal work:

  • Saving anything in the admin (products, categories, brands, menus, sections, sliders, banners, pages, posts, reviews, flash sales, coupons, settings, appearance, translations) refreshes every cached page and fragment.
  • Changes made outside the admin through the application (imports, the REST API, the demo installer, php artisan commands) refresh the cache too, because Sellora watches the catalogue and content records.
  • Stock and sales counters do not refresh the cache on every order, so a busy store keeps its cache. Product pages show the new stock after at most the page lifetime; the cart and checkout always check the real stock.
  • Time-based changes (a flash sale starting or ending, special price dates, "new" badges) appear after at most the page lifetime. Lower Keep pages for (minutes) if you need them sooner.

Settings > Performance#

Field Notes
Enable the full-page cache On by default. Turn it off while you edit theme files.
Keep pages for (minutes) Default 10. Also the longest delay for time-based changes.
Serve cached pages before the application starts Early hits (file cache only). Fastest option; turn off only when a plugin must run on every request.
Browser cache (seconds) Default 0: browsers revalidate every time with ETag (recommended).
Warm the cache every 30 minutes Requests the main pages from the cron job so visitors never wait for a page to be built.
Never cache these paths Paths (with * wildcards) that must always be built fresh, e.g. page/live-stock.
Cache menus, footers, listings ... Fragment caches. Leave on.

Below the form:

  • Cache status: driver, number and size of cached pages, OPcache, optimized files, queue, Redis, last warm-up.
  • Warm cache: requests the home page, shop, every category and brand, the 60 best-selling products, blog and CMS pages. The same is available as php artisan sellora:cache-warm (add -v to list the pages). The built-in php artisan serve server answers one request at a time and cannot warm itself; use the command there.
  • Clear page cache: drops every cached page and fragment.
  • Optimize: runs config:cache, route:cache and event:cache (php artisan optimize in a terminal also precompiles the views). Run it again after editing .env or updating Sellora. Clear optimized files undoes it.
  • Recommendations: what to change on this server for more speed.

Server checklist#

OPcache#

OPcache keeps compiled PHP in memory and makes every request two to four times faster. Most hosts enable it; check Settings > Performance > Cache status. Recommended php.ini values:

opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000
opcache.validate_timestamps=1
opcache.revalidate_freq=60
; optional, PHP 8: JIT
opcache.jit=tracing
opcache.jit_buffer_size=64M

With opcache.validate_timestamps=0 (fastest) you must restart PHP-FPM after every update or file change.

Optimize after every update#

php artisan optimize        # config, routes, events, views
php artisan optimize:clear  # undo (also clears the page cache)

.env is no longer read once the configuration is cached: after any .env change, run php artisan optimize again (or click Optimize).

Composer#

If you install or update the dependencies with Composer yourself, use the optimized, production autoloader:

composer install --no-dev --optimize-autoloader --classmap-authoritative

Redis (optional)#

For busy stores on a VPS, Redis is faster than files for the cache and sessions. Install Redis and the phpredis extension, then set in .env:

CACHE_STORE=redis
SESSION_DRIVER=redis
REDIS_HOST=127.0.0.1
REDIS_PORT=6379

and run php artisan optimize. The page cache works the same way with Redis (invalidation uses a version key, not tags). Early hits need the file driver; with Redis, cached pages are answered by the application after a minimal start (still about ten times faster than building the page).

Queue (optional)#

By default e-mails are sent while the customer waits (QUEUE_CONNECTION=sync). To send order and newsletter e-mails in the background, set QUEUE_CONNECTION=database in .env and run php artisan optimize. The cron job then runs queue:work --stop-when-empty every minute. Order e-mails are only sent after the order is saved.

Apache#

public/.htaccess already contains a block labelled C1 performance headers: long-lived immutable caching for files requested with a version (?v=…), 1 day for other CSS/JS, 30 days for images and fonts, and gzip compression for HTML, CSS, JS, JSON and SVG (needs mod_headers, mod_deflate).

Nginx#

gzip on;
gzip_types text/plain text/css application/javascript application/json application/xml image/svg+xml;

location ~* \.(css|js|mjs|woff2?|ttf|otf|svg|png|jpe?g|gif|webp|avif|ico)$ {
    if ($arg_v) { add_header Cache-Control "public, max-age=31536000, immutable"; }
    expires 30d;
    access_log off;
    try_files $uri =404;
}

Do not add fastcgi_cache for the whole site: personal pages must never be shared. Sellora's own page cache already knows who may receive a cached copy.

Troubleshooting: "I changed X and the storefront did not update"#

  1. Check who you are. Signed in as admin? You always see fresh pages; check in a private window instead, where you are a normal visitor.
  2. Look at the header. In the browser's developer tools (Network tab, the document request) find X-Sellora-Cache. HIT means a cached copy; MISS or BYPASS means it was built now.
  3. You edited a file (Blade view, theme CSS, config/*.php, .env): files are not watched. Click Clear page cache, and Optimize if you changed config or .env. After editing theme files, php artisan view:clear too.
  4. You changed the database directly (phpMyAdmin, SQL import): Sellora cannot see that. Click Clear page cache.
  5. A time-based change (sale start, special price date): it appears after at most Keep pages for minutes.
  6. Stock shows the old number: stock changes from orders refresh with the page lifetime; editing the product in the admin refreshes at once.
  7. Still old after clearing: a CDN or proxy in front of the store (Cloudflare "Cache Everything", Varnish) may hold its own copy. Purge it there, and bypass its cache when the cookies sl_nc or sellora-session (the store session) are present.
  8. Everything else fails: turn Enable the full-page cache off, check that the problem is gone, and contact support with the page address.
Sellora 1.1.0 documentation · 2026-10-07 · Printed or saved copy? The latest version is always in the Documentation folder of your download.