Skip to content

Server Configuration

This document covers web server configuration for serving Capell’s static HTML cache, recommended settings for production, and development environment setup.


Capell’s frontend generates static HTML files for every published page and stores them in public/page-cache/. For maximum performance, configure your web server to check for and serve these files directly — bypassing PHP entirely. This means TTFB (time to first byte) is determined by disk I/O and your server, not PHP execution time.

If no cached file exists (e.g. for a page that hasn’t been generated yet, or after a cache purge), the request falls through to PHP as normal and Capell generates the page dynamically, then queues a job to save the cached version.

Capell refuses to write a public HTML cache file when the rendered response contains explicit authoring markers, field paths, model IDs, or signed admin editor URLs. Those responses are sent with Cache-Control: private, no-store and X-Frontend-Cache: BYPASS. Treat this as a deployment blocker: fix the Blade/theme/package output rather than trying to force the cache write.


Add these rules to your .htaccess file, before the standard Laravel rewrite rules:

# Serve static HTML cache if the file exists
RewriteCond %{DOCUMENT_ROOT}/page-cache%{REQUEST_URI}.html -f
RewriteRule ^ page-cache%{REQUEST_URI}.html [L]

Full .htaccess example:

<IfModule mod_rewrite.c>
RewriteEngine On
# Serve cached HTML if it exists
RewriteCond %{DOCUMENT_ROOT}/page-cache%{REQUEST_URI}.html -f
RewriteRule ^ page-cache%{REQUEST_URI}.html [L]
# Standard Laravel rewrite
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
</IfModule>

Add the static HTML check to your location / block:

location / {
try_files $uri $uri/ /page-cache$uri.html /index.php?$query_string;
}

Full server block example:

server {
listen 80;
server_name example.com;
root /var/www/html/public;
index index.php;
location / {
try_files $uri $uri/ /page-cache$uri.html /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.(?!well-known).* {
deny all;
}
}

After making bulk changes or restoring from a database backup:

Terminal window
# Clear just the HTML page cache
php artisan capell:html-cache:clear
# Clear everything (config, views, routes, page cache)
php artisan optimize:clear

From the admin panel, use the Clear Cache button in the top navigation bar.

Capell automatically purges cache entries when content changes are published. The following events trigger targeted invalidation:

  • Publishing a page purges that page’s cached HTML.
  • Updating a page’s slug purges the page, all its descendants, and any listing pages.
  • Updating site settings, translations, themes, or media purges cached URLs that used those records.
  • Site setting changes also match cached URLs by the site’s configured domains, so root pages such as the homepage are cleared even if the model index missed an explicit Site dependency.
  • Changes to global navigation purge pages that render navigation.

After a full clear, regenerate the cache:

Terminal window
# Regenerate all sites
php artisan capell:static-site
# Regenerate for one site
php artisan capell:static-site --site=1

Lockdown is designed for incidents where PHP middleware alone is not enough because the web server may serve public/page-cache directly. When Lockdown is enabled, Capell renames the live page cache directory to a preserved sibling, creates a fresh page-cache directory, and writes Lockdown HTML into the same cached paths. When Lockdown is disabled, the temporary Lockdown cache is removed and the preserved live cache is moved back.

Do not clear public/page-cache during Lockdown unless you are deliberately accepting a cold cache rebuild after recovery. See Lockdown for the operator runbook.


The page cache uses a dedicated storage disk. Ensure it is configured in config/filesystems.php:

'disks' => [
'page_cache' => [
'driver' => 'local',
'root' => public_path('page-cache'),
'throw' => false,
],
],

The public/page-cache/ directory must be writable by the web server process.


To skip caching in development, add to your .env:

DEBUG_SKIP_CACHE=true
CAPELL_HTML_CACHE=false
HTML_MINIFY=false

DEBUG_SKIP_CACHE=true bypasses cache reads and writes on every request without disabling the cache system. This is the simplest and safest option for local development.


  • Enable HTTP/2 on your server to allow multiplexed requests.
  • Set appropriate cache headers for static assets (CSS, JS, images) — these should be long-lived.
  • Consider a CDN in front of your server to serve cached HTML from edge locations.
  • Use opcache for PHP to speed up the dynamic fallback path.
  • For very high-traffic sites, consider a Varnish or Nginx proxy cache in front of the origin as a second caching layer.