Skip to content
Sign in

Settings reference

Every key of Settings.json with its type, default, effect and an example, the environment overrides, a complete production configuration and the Google Analytics measurement id.

Where the settings live

All configuration is one JSON object in src/Config/Settings.json. The file is not committed: bin/wbe install creates it from src/Config/settings.json.default. The framework reads it once per request; a missing key falls back to the default listed below, and a malformed value in http, analytics or the page cache policy throws an error instead of being ignored. After changing the file clear the cache (POST /api/v2/cache/flush, an integration key with the cache:write scope) and, when renderer.compileCheck is off, the compiled template directory. Check the result with vendor/bin/wbengine.php config:check --config=src/Config/Settings.json --root=src.

Secrets can come from the environment instead of the file: WBENGINE_RECAPTCHA_SECRET, WBENGINE_DEBUG and WBENGINE_IMAGE_DIR. An environment variable that is set and not empty wins over the file.

General

Key Type, default Effect
timeZone string, Europe/Prague PHP time zone of the application. Required.
debug bool, false Shows the debug bar and detailed errors. Never true in production. WBENGINE_DEBUG overrides it.

Renderer

Key Type, default Effect
renderer.adapterName string, NativeRenderer NativeRenderer or the optional SmartyRenderer.
renderer.autoEscape bool, true Escapes the output of template variables. false is only a migration aid for old templates.
renderer.templatesDir string, /View/ Template directory under src/.
renderer.compiledDir string, Compiled/ Compiled templates, must be writable by the PHP user.
renderer.configDir string, /Config/ Template configuration directory.
renderer.compileCheck bool, true Checks on every request whether a template changed. Set false in production and empty the compiled directory on deploy.

Database

dbAdapterDefinition is an object: adapterName (Mysqli), hostname, port, database, username, password, charset. The default file points to the Docker host db with development credentials; replace all of them. Use utf8mb4 as charset on a new installation. There is no environment override for the database password, so protect the file (chmod 640).

Secrets

Key Type, default Effect
recaptcha.secretKey string, empty Secret of the login form captcha. Empty means the captcha check fails closed. Overridden by WBENGINE_RECAPTCHA_SECRET. With recaptcha.enabled: true the doctor command requires it.

Proxies and HTTPS

Key Type, default Effect
http.trustedProxies array, [] Exact addresses or CIDR ranges of reverse proxies whose forwarding headers are believed. Empty means every header is ignored.
http.forwardedHeaders array, [] Opt-in headers: x-forwarded-for (client address) and x-forwarded-proto (original scheme). Other values are rejected.

Behind host Nginx or a load balancer set both, otherwise the site thinks every visitor has the proxy address and uses plain HTTP: session cookies are not secure, rate limit keys are shared and /integrations/ refuses to issue a key. The proxy must overwrite X-Forwarded-Proto and build X-Forwarded-For from the real connection.

"http": { "trustedProxies": ["172.18.0.1/32"], "forwardedHeaders": ["x-forwarded-for", "x-forwarded-proto"] }

Languages

Key Type, default Effect
i18n.enabled bool, false (the skeleton sets true) Turns languages on.
i18n.default string, en Language of unprefixed URLs and fallback for missing translations.
i18n.locales array, ["en"] Offered languages, for example ["en", "cs"]. The default is always included.
i18n.detect array, ["url", "cookie", "header"] Detection order. The skeleton uses ["url"] so unprefixed URLs are always English. Add cookie or header to let a saved choice or the browser language win.
i18n.urlPrefix bool, true Uses /cs/... prefixes for non-default languages.
i18n.prefixDefault bool, false Also prefixes the default language (/en/...).
i18n.cookie string, wb_locale Name of the preference cookie (letters, digits, underscore).
i18n.cookieDays int, 365 Lifetime of that cookie.
i18n.dir string, /Locale/ Catalog directory under src/, with en.php, cs.php.
i18n.baseUrl string, empty Public address such as https://example.com. Used for absolute hreflang links instead of the Host header. Set it in production.

Caching

Key Type, default Effect
cache.enabled bool, false (the skeleton sets true) Data and fragment cache.
cache.driver string, file file or apcu. apcu needs the extension and a PHP-FPM restart to clear.
cache.ttl int seconds, 300 Lifetime of data cache entries.
cache.dir string, temp directory Directory of the file driver. Default is a subdirectory of the system temp directory, which can vanish under PrivateTmp: set an absolute path.
cache.pages.enabled bool, false Whole-page cache for anonymous requests.
cache.pages.ttl int seconds, 60 Lifetime of cached pages.
cache.pages.allowPaths array, omitted Exact canonical paths allowed in the page cache. Omitted allows other eligible paths, empty [] disables page caching.
cache.pages.denyPaths array, omitted Paths never cached. Wins over allowPaths.

Paths are compared ignoring case and a trailing slash, invalid policies fail closed. Private, Set-Cookie and personal responses are never stored whatever the policy says. Example: "allowPaths": ["/", "/docs/", "/blog/"], "denyPaths": ["/profile/", "/login/"]. A broken cache never takes the site down: it is disabled and logged.

Rate limits and counters

Key Type, default Effect
rateLimit.login.enabled bool, true Limits failed logins.
rateLimit.login.maxAttempts int, 5 Attempts per client address and account together.
rateLimit.login.ipMaxAttempts int, 20 Attempts per client address over all accounts.
rateLimit.login.accountMaxAttempts int, 25 Attempts per account from all addresses (distributed guessing).
rateLimit.login.window int seconds, 900 Length of the counting window.
rateLimit.login.dir string, temp directory Counter files. Set an absolute, writable path.
counters.views.enabled bool, false Batches the article view counter instead of one database write per view.
counters.views.hits int, 50 Hits per batch.
counters.views.seconds int, 60 Longest delay of a batch.
counters.views.dir string, temp directory Counter files. Set an absolute, writable path.

The rate limit uses the client address, so it only works correctly when http.trustedProxies is right behind a proxy.

Native analytics

Key Type, default Effect
analytics.enabled bool, false Collects identity-free daily aggregates (no cookies, no addresses). Needs the analytics tables (see the Deployment page).
analytics.timezone string, UTC IANA time zone in which days are cut. Invalid values throw.
analytics.retentionDays int, 62 How long daily rows are kept, 31 to 365. Run analytics:prune from cron daily.

This is the privacy-friendly first-party measurement. It is independent of Google Analytics below: you can run either, both or none. A policy that fits most sites: native analytics on with retentionDays 62 (it sets no cookies and stores no addresses), Google Analytics off unless the operator has a legal basis and a consent process. Decide the legal side for your own site.

Google Analytics

Key Type, default Effect
googleAnalytics.measurementId string, empty Measurement id such as G-XXXXXXXXXX. Empty, missing or malformed disables everything.

The skeleton does not load Google's tag on its own. When the id is set, the layout renders the cookie banner and the footer link "Cookie settings", and public/cookie-consent.js loads gtag.js only after the visitor presses "Allow analytics". The choice is kept in the browser (localStorage), "Reject" removes the analytics cookies, and the tag is never loaded without consent. With the key empty the layout contains no banner, no button and no script. The text of the banner is in the locale catalogs (cookies.*); adapt it to your policy. A page cached before you set the id keeps its old markup until the cache is flushed.

"googleAnalytics": { "measurementId": "G-XXXXXXXXXX" }

Audit and change log

Key Type, default Effect
audit.retentionDays int, 365 How long cms_api_audit and cms_api_changes rows are kept, 30 to 3650. Invalid values throw.
audit.revisionRetentionDays int, 365 How long cms_api_revisions (article draft history) are kept, 30 to 3650.

Nothing is deleted automatically: run audit:prune from cron (--dry-run only counts, --days=N overrides both keys). The newest change of every resource and every revision a draft still uses are always kept. The tables need the change log columns (sh bin/update-database.sh DATABASE) when API v2 is enabled. What is recorded is described in Logging and audit.

Search engines

Key Type, default Effect
seo.allowIndexing bool, true false closes the whole site to crawlers, for a staging copy: robots.txt says Disallow: / and /sitemap.xml answers 404.
seo.disallow list of paths, empty Paths named in robots.txt, each starting with /.
seo.sitemapExclude list of page links, empty Pages left out of /sitemap.xml, each starting with /.

/robots.txt and /sitemap.xml are generated for every request and registered in public/index.php. The sitemap lists the visible public pages once per language, with links to the other languages, and leaves out pages that need a login and pages with a hidden article. A language is listed only when the page has a translation. Pages that take parameters in the address are listed by their own address only.

Images and the API

Key Type, default Effect
apiV2.enabled bool, false (the skeleton sets true) Turns on the server-to-server API v2 used by the central Admin. Needs HTTPS and the API tables.
apiV2.imageDirectory string, unset Absolute directory for images uploaded through API v2. Unset means image upload is unavailable. Overridden by WBENGINE_IMAGE_DIR. Keep it outside public/, writable by PHP. Files are PNG, JPEG or WebP up to 512 KiB.

Production example

A complete file for a site behind host Nginx, with every secret in the environment (WBENGINE_RECAPTCHA_SECRET) so the placeholders below are not real keys:

{
  "timeZone": "Europe/Prague",
  "debug": false,
  "apiV2": {
    "enabled": true,
    "imageDirectory": "/var/lib/wbengine/images"
  },
  "renderer": {
    "adapterName": "NativeRenderer",
    "autoEscape": true,
    "templatesDir": "/View/",
    "compiledDir": "Compiled/",
    "configDir": "/Config/",
    "compileCheck": false
  },
  "dbAdapterDefinition": {
    "adapterName": "Mysqli",
    "hostname": "127.0.0.1",
    "port": "3306",
    "database": "wbengine_skeleton_app",
    "username": "wbengine",
    "password": "SET-A-LONG-RANDOM-PASSWORD",
    "charset": "utf8mb4"
  },
  "recaptcha": { "secretKey": "" },
  "http": {
    "trustedProxies": ["127.0.0.1"],
    "forwardedHeaders": ["x-forwarded-for", "x-forwarded-proto"]
  },
  "cache": {
    "enabled": true,
    "driver": "file",
    "ttl": 300,
    "dir": "/var/cache/wbengine",
    "pages": {
      "enabled": true,
      "ttl": 60,
      "denyPaths": ["/login/", "/profile/", "/integrations/"]
    }
  },
  "i18n": {
    "enabled": true,
    "default": "en",
    "locales": ["en", "cs"],
    "detect": ["url"],
    "baseUrl": "https://example.com"
  },
  "rateLimit": { "login": { "dir": "/var/lib/wbengine/ratelimit" } },
  "counters": { "views": { "enabled": true, "dir": "/var/lib/wbengine/counters" } },
  "analytics": { "enabled": true, "timezone": "Europe/Prague", "retentionDays": 62 },
  "audit": { "retentionDays": 365, "revisionRetentionDays": 365 },
  "googleAnalytics": { "measurementId": "" }
}

Where each part is deployed (permissions of the directories, proxies, cron) is described in Deployment, and the caching behavior in Caching.