Ava CMS's configuration is simple and transparent. All settings live in app/config/ as plain PHP files.
Quick Start: The settings most people change first
Open app/config/ava.php and update these:
'site' => [
'name' => 'My Awesome Site',
'base_url' => 'https://example.com', // Full URL, no trailing slash
'timezone' => 'Europe/London', // php.net/timezones
'locale' => 'en_GB', // php.net/setlocale
'date_format' => 'F j, Y', // php.net/datetime.format
],
See Site Identity below for details on each option, or the PHP date format reference for date formatting.
Why PHP Configs?
We use PHP arrays instead of YAML or JSON because:
- It's Readable: You can add comments to explain why you changed a setting.
- It's Powerful: You can use constants, logic, or helper functions right in your config.
- It's Standard: No special parsers or hidden
.envfiles to debug.
The Config Files
| File | What it controls |
|---|---|
| ava.php | Main site settings (name, URL, cache, themes, plugins, security). |
| content_types.php | Defines your content types (Pages, Posts, etc.). See also Writing Content. |
| taxonomies.php | Defines how you group content (Categories, Tags). See also Taxonomy Fields. |
users.php |
Admin users (managed automatically by CLI). See User Management. |
Main Settings (ava.php)
This is where you set up your site's identity and behavior.
Site Identity
return [
'site' => [
'name' => 'My Awesome Site',
'base_url' => 'https://example.com', // Full URL, no trailing slash
'timezone' => 'Europe/London', // php.net/timezones
'locale' => 'en_GB', // php.net/setlocale
'date_format' => 'F j, Y', // php.net/datetime.format
],
// ...
];
| Option | Type | Default | Description |
|---|---|---|---|
site.name |
string | 'My Ava Site' |
Your site's display name. Used in templates, RSS feeds, sitemaps, and admin. |
site.base_url |
string | required | Full URL (no trailing slash). Used for sitemaps, canonical URLs, and absolute links. |
site.timezone |
string | 'UTC' |
Timezone for dates. Use a PHP timezone identifier. |
site.locale |
string | 'en_GB' |
Locale for date/number formatting. See PHP locale codes. |
site.date_format |
string | 'F j, Y' |
Default format for $ava->date(). Uses PHP date() format codes. |
In templates: Access site info via $site['name'], $site['url'], and $site['timezone']. See Theming - Template Variables.
Paths
Directory locations for your content, themes, plugins, and other assets.
'paths' => [
'content' => 'content', // Where your Markdown files live
'themes' => 'app/themes', // Where theme folders live
'plugins' => 'app/plugins', // Where plugin folders live
'snippets' => 'app/snippets', // Snippets for [snippet] shortcode
'storage' => 'storage', // Cache, logs, and temporary files
'aliases' => [
'@media:' => '/media/',
],
],
| Option | Type | Default | Description |
|---|---|---|---|
paths.content |
string | 'content' |
Directory containing your Markdown content files. |
paths.themes |
string | 'app/themes' |
Directory containing theme folders. |
paths.plugins |
string | 'app/plugins' |
Directory containing plugin folders. |
paths.snippets |
string | 'app/snippets' |
Directory for PHP snippets. See Shortcodes - Snippets. |
paths.storage |
string | 'storage' |
Directory for cache files, logs, and temporary data. |
paths.aliases |
array | ['@media:' => '/media/'] |
Path aliases for use in content. See Writing Content - Images and Media. |
All paths are relative to your project root unless they start with /.
Theme
'theme' => 'default',
| Option | Type | Default | Description |
|---|---|---|---|
theme |
string | 'default' |
The active theme folder name inside app/themes/. |
See: Theming for theme structure, template variables, and development documentation.
Content Index
The content index is a binary snapshot of your content metadata—used to avoid parsing Markdown files on every request.
'content_index' => [
'mode' => 'auto', // When to rebuild: auto, never, always
'backend' => 'array', // Storage: array or sqlite
'use_igbinary' => true, // Faster serialization if available
'prerender_html' => true, // Pre-render Markdown during rebuild
],
| Option | Type | Default | Description |
|---|---|---|---|
mode |
string | 'auto' |
auto = rebuild when files change, never = rebuild via CLI/admin dashboard only (production), always = every request (debug only). |
backend |
string | 'array' |
array = binary PHP arrays (default), sqlite = SQLite database (for 10k+ items). |
use_igbinary |
bool | true |
Use igbinary extension for faster serialization if installed. |
prerender_html |
bool | true |
Pre-render Markdown → HTML during rebuild to speed up uncached requests. |
See: Performance - Content Indexing for detailed explanations of modes, backends, tiered caching, and benchmarks.
Webpage Cache
The webpage cache stores fully-rendered HTML for near-instant serving.
'webpage_cache' => [
'enabled' => true,
'ttl' => null, // Seconds, or null = until rebuild
'exclude' => ['/api/*', '/preview/*'], // URL patterns to never cache
],
| Option | Type | Default | Description |
|---|---|---|---|
enabled |
bool | true |
Enable HTML webpage caching. Recommended true for production. |
ttl |
int|null | null |
Cache lifetime in seconds. null = cached until next rebuild. |
exclude |
array | ['/api/*', '/preview/*'] |
URL patterns to never cache. Supports glob wildcards (*). |
See: Performance - Webpage Caching for how it works, fast-path optimization, and cache management.
Routing
'routing' => [
'trailing_slash' => false,
],
| Option | Type | Default | Description |
|---|---|---|---|
trailing_slash |
bool | false |
URL style: false = /about, true = /about/. Mismatches trigger 301 redirects. |
See: Routing for URL styles, custom routes, and taxonomy URLs.
Content Parsing
Controls how Ava CMS processes your Markdown content files.
'content' => [
'frontmatter' => [
'format' => 'yaml',
],
'markdown' => [
'allow_html' => true,
'heading_ids' => true,
'disallowed_tags' => ['script', 'noscript'],
],
'id' => [
'type' => 'ulid',
],
],
| Option | Type | Default | Description |
|---|---|---|---|
frontmatter.format |
string | 'yaml' |
Frontmatter parser format. Currently only YAML is supported. |
markdown.allow_html |
bool | true |
Allow raw HTML tags in Markdown content. |
markdown.heading_ids |
bool | true |
Add id attributes to headings for deep linking. |
markdown.disallowed_tags |
array | ['script', 'noscript'] |
HTML tags to strip even when allow_html is true. |
id.type |
string | 'ulid' |
ID format for new content: 'ulid' (recommended) or 'uuid7'. |
See: Writing Content for frontmatter fields and Markdown syntax.
Security
Ava includes security settings for shortcodes, preview tokens, and HTTP security headers.
'security' => [
'shortcodes' => [
'allow_php_snippets' => true,
],
'headers' => [
'content_security_policy' => [
"default-src 'self'",
"base-uri 'none'",
"object-src 'none'",
"frame-ancestors 'none'",
"frame-src 'self' https://www.youtube.com https://www.youtube-nocookie.com https://player.vimeo.com",
"form-action 'self'",
"connect-src 'self' https:", // External APIs, analytics
"img-src 'self' data: https:", // External images
"font-src 'self' data: https:", // Google Fonts, CDNs
"style-src 'self' 'unsafe-inline' https:",
"script-src 'self' 'unsafe-inline' https:",
],
'permissions_policy' => [
'camera=()',
'microphone=()',
'geolocation=()',
'payment=()',
'usb=()',
'interest-cohort=()',
],
],
'preview_token' => null,
],
| Option | Type | Default | Description |
|---|---|---|---|
shortcodes.allow_php_snippets |
bool | true |
Enable the shortcode for including PHP files from app/snippets/. |
headers.content_security_policy |
array | See above | CSP directives applied to public responses. See CSP explanation below. |
headers.permissions_policy |
array | See above | Browser permissions to disable (camera, mic, etc.). |
preview_token |
string|null | null |
Secret token for previewing draft content without logging in. |
Preview token usage: Access drafts via https://example.com/path?preview=1&token=your-token
Understanding Content Security Policy
Content Security Policy (CSP) is a security layer that helps protect your site from attacks like cross-site scripting (XSS) and data injection. It works by telling browsers which sources of content are allowed to load.
How it works: When a browser loads your page, it reads the CSP header and blocks any resources (scripts, styles, images, etc.) that don't match the allowed sources. This stops attackers from injecting malicious scripts, even if they find a vulnerability in your site.
Ava's default CSP explained:
| Directive | Value | What it does |
|---|---|---|
default-src 'self' |
Only your domain | Fallback rule: only load resources from your own site. |
script-src 'self' 'unsafe-inline' https: |
Your domain + inline + HTTPS | JavaScript from your files, inline scripts, and any HTTPS source. Allows analytics, widgets, etc. |
style-src 'self' 'unsafe-inline' https: |
Your domain + inline + HTTPS | CSS from your files, inline style="" attributes, and any HTTPS stylesheet URLs. |
img-src 'self' data: https: |
Your domain + data URIs + HTTPS | Images from your site, embedded base64 data URIs, or any HTTPS image URL (shields.io badges, external photos, etc.). |
font-src 'self' data: https: |
Your domain + data URIs + HTTPS | Fonts from your site, embedded data URIs, or any HTTPS font URL (Google Fonts, etc.). |
connect-src 'self' https: |
Your domain + HTTPS | AJAX/fetch requests to your site and any HTTPS API (analytics, external services). |
frame-src 'self' youtube vimeo |
Your domain + video platforms | Allows embedding YouTube and Vimeo videos by default. |
form-action 'self' |
Only your domain | Forms can only submit to your own site. |
frame-ancestors 'none' |
Nobody | Your site cannot be embedded in iframes (prevents clickjacking). |
object-src 'none' |
Blocked | No Flash, Java applets, or other plugins. |
base-uri 'none' |
Blocked | Prevents <base> tag injection attacks. |
Why these defaults? Ava's CSP is designed to balance security with flexibility for a content management system. The defaults are permissive enough for common use cases:
- Embedding YouTube and Vimeo videos
- Loading images from external sources
- Loading fonts from CDNs like Google Fonts
- Adding analytics scripts (Google Analytics, Plausible, Matomo, etc.)
- Connecting to external APIs
This is more permissive than a locked-down web app, but appropriate for a CMS where content flexibility is expected. For high-security sites, restrict https: to specific domains.
When you might need to customize:
- Embedding other video platforms? Add their domains to
frame-src. - Need your site to be embeddable? Change
frame-ancestors 'none'to list allowed parent domains. - Need stricter security? Replace
https:with specific domains (e.g.,https://cdn.example.com).
Example: Stricter CSP with specific domains only
'headers' => [
'content_security_policy' => [
"default-src 'self'",
"base-uri 'none'",
"object-src 'none'",
"frame-ancestors 'none'",
"frame-src 'self' https://www.youtube.com",
"form-action 'self'",
"connect-src 'self' https://www.google-analytics.com",
"img-src 'self' data:",
"font-src 'self' data: https://fonts.gstatic.com",
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
"script-src 'self' https://www.googletagmanager.com",
],
// ... other headers
],
Testing your CSP: Open your browser's developer tools (F12) and check the Console tab. Any blocked resources will show as errors, telling you exactly which directive blocked them and what source was attempted.
Learn more: MDN Content Security Policy Guide
Permissions Policy disables browser features you don't need. The defaults block camera, microphone, geolocation, payment APIs, USB access, and FLoC tracking. Remove items from the array if your site legitimately needs these features.
HSTS (Strict Transport Security) should be configured at your web server level (Apache, Nginx, Caddy), not in Ava's config.
See: Shortcodes - Snippets and Routing - Preview Mode.
Admin Dashboard
The admin dashboard provides a web-based interface for managing your site.
'admin' => [
'enabled' => false,
'path' => '/ava-admin',
'theme' => 'cyan',
'session' => [
'timeout' => 1800, // 30 minutes
'ip_binding' => false,
],
'media' => [
'enabled' => true,
'path' => 'public/media',
'organize_by_date' => true,
'max_file_size' => 10 * 1024 * 1024, // 10 MB
'allowed_types' => [
'image/jpeg', 'image/png', 'image/gif',
'image/webp', 'image/svg+xml', 'image/avif',
],
],
],
| Option | Type | Default | Description |
|---|---|---|---|
enabled |
bool | false |
Enable the admin dashboard. |
path |
string | '/ava-admin' |
URL path for the admin area. Change to obscure admin location. |
theme |
string | 'cyan' |
Color theme: cyan, pink, purple, green, blue, or amber. |
session.timeout |
int|null | 1800 |
Session lifetime in seconds. null disables timeout. |
session.ip_binding |
bool | false |
Lock session to login IP. See Admin - IP Binding for warnings. |
media.enabled |
bool | true |
Enable the media upload feature. |
media.path |
string | 'public/media' |
Upload directory (relative to project root). |
media.organize_by_date |
bool | true |
Create /year/month/ subfolders for uploads. |
media.max_file_size |
int | 10485760 |
Maximum file size in bytes (10 MB default). |
media.allowed_types |
array | See code | Array of allowed MIME types for uploads. |
./ava user:add before enabling the admin dashboard.
See: Admin Dashboard for features, security, and user management.
Debug Mode
Control error visibility and logging for development and troubleshooting.
'debug' => [
'enabled' => false,
'display_errors' => false,
'log_errors' => true,
'level' => 'errors',
],
| Option | Type | Default | Description |
|---|---|---|---|
enabled |
bool | false |
Master switch for debug features. |
display_errors |
bool | false |
Show PHP errors in browser output. Never enable in production! |
log_errors |
bool | true |
Write errors to storage/logs/error.log. |
level |
string | 'errors' |
Error reporting: all (dev), errors (prod), or none. |
display_errors in production—it can expose sensitive information.
Logs
Control log file size and automatic rotation.
'logs' => [
'max_size' => 10 * 1024 * 1024, // 10 MB
'max_files' => 3,
],
| Option | Type | Default | Description |
|---|---|---|---|
max_size |
int | 10485760 |
Maximum log file size in bytes before rotation (10 MB). |
max_files |
int | 3 |
Number of rotated log files to keep. |
See: CLI - Logs for log viewing and management commands.
CLI Appearance
'cli' => [
'theme' => 'cyan',
],
| Theme | Description |
|---|---|
cyan |
Cool cyan/aqua (default) |
pink, purple, green, blue, amber |
Alternative colors |
disabled |
No colors (for CI/CD or non-ANSI terminals) |
Plugins
'plugins' => [
'sitemap',
'feed',
'redirects',
],
Array of plugin folder names to activate. Plugins load in the order listed.
See: Bundled Plugins for sitemap, RSS feed, and redirects configuration.
Custom Settings
Add your own site-specific configuration. Access values in templates with $ava->config():
// In ava.php
'analytics' => [
'tracking_id' => 'G-XXXXXXXXXX',
'enabled' => true,
],
// In templates
<?php if ($ava->config('analytics.enabled')): ?>
<!-- Analytics code for <?= $ava->config('analytics.tracking_id') ?> -->
<?php endif; ?>
Content Types: content_types.php
Define what kinds of content your site has. Each content type specifies where files live, how URLs are generated, and which templates to use.
<?php
return [
'page' => [
'label' => 'Pages',
'icon' => 'description',
'content_dir' => 'pages',
'url' => [
'type' => 'hierarchical',
'base' => '/',
],
'templates' => [
'single' => 'page.php',
],
'taxonomies' => [],
'fields' => [],
'sorting' => 'manual',
'search' => [
'enabled' => true,
'fields' => ['title', 'body'],
],
],
'post' => [
'label' => 'Posts',
'icon' => 'article',
'content_dir' => 'posts',
'url' => [
'type' => 'pattern',
'pattern' => '/blog/{slug}',
'archive' => '/blog',
],
'templates' => [
'single' => 'post.php',
'archive' => 'archive.php',
],
'taxonomies' => ['category', 'tag'],
'fields' => [],
'sorting' => 'date_desc',
'search' => [
'enabled' => true,
'fields' => ['title', 'excerpt', 'body'],
],
],
];
Content Type Options
| Option | Type | Required | Description |
|---|---|---|---|
label |
string | Yes | Human-readable name shown in admin UI. |
icon |
string | No | Material icon name for admin UI. |
content_dir |
string | Yes | Folder inside content/ where files for this type live. |
url |
array | Yes | URL generation settings. See Routing - URL Styles. |
templates |
array | Yes | Template file mappings (single, archive). |
taxonomies |
array | No | Which taxonomies apply to this type. Default: [] |
fields |
array | No | Custom field definitions for validation and admin UI. See Fields. |
sorting |
string | No | Default sort: date_desc, date_asc, title, or manual. |
cache_fields |
array | No | Extra frontmatter fields to include in archive cache for fast access. |
search |
array | No | Search configuration (enabled, fields, weights). See Search. |
Example with Custom Fields
Here's a more complete example showing custom fields for a blog post type:
'post' => [
'label' => 'Blog Posts',
'content_dir' => 'posts',
'url' => [
'type' => 'pattern',
'pattern' => '/blog/{slug}',
'archive' => '/blog',
],
'templates' => [
'single' => 'post.php',
'archive' => 'archive.php',
],
'taxonomies' => ['category', 'tag'],
'sorting' => 'date_desc',
// Custom fields for validation and admin UI
'fields' => [
'author' => [
'type' => 'text',
'label' => 'Author Name',
'required' => true,
],
'featured_image' => [
'type' => 'text',
'label' => 'Featured Image URL',
],
'reading_time' => [
'type' => 'number',
'label' => 'Reading Time (minutes)',
'min' => 1,
'max' => 60,
'default' => 5,
],
'featured' => [
'type' => 'checkbox',
'label' => 'Featured Post',
'default' => false,
],
],
// Include these fields in archive cache for fast listing access
'cache_fields' => ['author', 'featured_image', 'reading_time', 'featured'],
],
See: Fields for all available field types (text, number, checkbox, select, date, etc.) and validation options.
Taxonomies: taxonomies.php
Taxonomies organize content into groups (categories, tags, authors, etc.).
<?php
return [
'category' => [
'label' => 'Categories',
'icon' => 'folder',
'hierarchical' => true,
'public' => true,
'rewrite' => [
'base' => '/category',
'separator' => '/',
],
'behaviour' => [
'allow_unknown_terms' => true,
'hierarchy_rollup' => true,
],
'ui' => [
'show_counts' => true,
'sort_terms' => 'name_asc',
],
],
'tag' => [
'label' => 'Tags',
'icon' => 'tag',
'hierarchical' => false,
'public' => true,
'rewrite' => [
'base' => '/tag',
],
'behaviour' => [
'allow_unknown_terms' => true,
],
'ui' => [
'show_counts' => true,
'sort_terms' => 'count_desc',
],
],
];
Taxonomy Options
| Option | Type | Default | Description |
|---|---|---|---|
label |
string | Required | Human-readable name for the taxonomy. |
icon |
string | null |
Material icon name for admin UI. |
hierarchical |
bool | false |
Support parent/child term relationships. |
public |
bool | true |
Create public archive pages for terms. |
rewrite.base |
string | '/{taxonomy}' |
URL prefix for term archives. |
rewrite.separator |
string | '/' |
Separator for hierarchical term paths. |
behaviour.allow_unknown_terms |
bool | true |
Auto-create terms when used in content. |
behaviour.hierarchy_rollup |
bool | true |
Include child terms when filtering by parent. |
ui.show_counts |
bool | true |
Display content count next to terms. |
ui.sort_terms |
string | 'name_asc' |
Default sort: name_asc, name_desc, count_asc, count_desc. |
See: Taxonomies for assigning terms to content, term storage, hierarchical terms, and template helpers.
Environment-Specific Config
Use PHP logic to override settings per environment:
// app/config/ava.php
$config = [
'site' => [
'name' => 'My Site',
'base_url' => 'https://example.com',
],
'content_index' => ['mode' => 'never'],
'debug' => ['enabled' => false],
];
// Development overrides
if (getenv('APP_ENV') === 'development') {
$config['site']['base_url'] = 'http://localhost:8000';
$config['content_index']['mode'] = 'auto';
$config['admin']['enabled'] = true;
$config['debug'] = [
'enabled' => true,
'display_errors' => true,
'level' => 'all',
];
}
return $config;
Set the environment variable in your server config:
export APP_ENV=development