Architecture
Routing
How Bolt CMS maps URLs to files using file-based routing, path sanitization, and automatic resolution.
How URLs Map to Files
Bolt CMS uses file-based routing. The URL path directly corresponds to a file or directory inside the pages/ folder. There is no route configuration file and no route registration step.
When a request comes in, Bolt strips the base path, sanitizes the URL, and looks for a matching file. If it finds one, the file is loaded. If not, a 404 is returned.
Resolution Priority
For a given URL path, Bolt checks for a matching file in this order:
pages/{path}.php— direct PHP file matchpages/{path}/index.php— directory with index file
The first match wins. If neither exists, Bolt sends a 404 Not Found response and exits.
Before the 404, resolution also falls back to any installed extensions. Core is always checked first, and a bare URL never serves an extension's page outright: if exactly one extension provides the route, Bolt redirects to its explicit /<slug>/{path} form; if two or more do, it returns a disambiguation page instead of guessing.
For sites that also support Markdown, the full resolution chain is:
pages/{path}.php— direct PHP filepages/{path}/index.php— directory with PHP indexpages/{path}.md— direct Markdown filepages/{path}/index.md— directory with Markdown index
Path Sanitization
Before attempting file resolution, Bolt sanitizes the incoming URL to prevent directory traversal and normalize the path:
- Query parameters stripped — everything after
?is removed. - Fragment identifiers stripped — everything after
#is removed. - Leading and trailing slashes trimmed.
- Consecutive slashes collapsed —
//becomes/. - Segments lowercased — URLs are case-insensitive.
- Non-alphanumeric characters removed — only letters, numbers, and hyphens survive in each segment.
// Sanitization in practice
$path = explode('?', $request_URI)[0]; // strip query
$path = explode('#', $path)[0]; // strip fragment
$path = rtrim(ltrim($path, '/'), '/'); // trim slashes
$path = preg_replace('/\/+/', '/', $path); // collapse slashes
$segments = explode('/', $path);
$segments = array_map('strtolower', array_map('trim', $segments));
$segments = array_map(function ($s) {
return preg_replace('/[^a-zA-Z0-9-]/', '', $s);
}, $segments);
$path = implode('/', $segments);
The $page Array
Every page file receives the $page array and is expected to populate it. The array has two top-level keys:
$page['config']
| Key | Type | Purpose |
|---|---|---|
title |
string | Browser tab title (<title> tag). |
description |
string | Meta description for SEO. |
keywords |
string | Meta keywords (comma-separated). |
header |
string | Which header file to load (default: 'default'). |
layout |
string | Which layout file to load (default: 'default'). |
footer |
string | Which footer file to load (default: 'default'). |
pageTitle |
string | The H1 heading rendered by the layout. |
pageDescription |
string | Subtitle text rendered below the H1. |
$page['content']
A string of HTML captured via output buffering. The layout template receives this and decides where to place it in the page structure.
Home Page Routing
The root URL (/) is a special case. Instead of sanitizing the path and resolving a file, Bolt directly loads pages/index.php:
if ($request_URI !== '/') {
// ... sanitize and resolve path
} else {
require(__DIR__ . '/pages/index.php');
}
This means pages/index.php always serves your home page. You cannot override this behavior.
404 Handling
If no matching file is found after the resolution chain, Bolt sends a 404 header and exits immediately:
header('HTTP/1.0 404 Not Found');
exit;
There is no built-in 404 page template. To show a custom 404 page, you would add a fallback require before the exit call to load a custom error page file.
Markdown Pages
If your site supports Markdown pages, files ending in .md are processed with frontmatter parsing. Frontmatter is defined between triple-dash delimiters at the top of the file:
---
title: About Us
description: Learn more about our team.
layout: default
---
# About Us
This is a Markdown page. It supports **bold**, *italic*, [links](https://example.com), and all standard Markdown syntax.
The frontmatter values are merged into $page['config']. The Markdown body is parsed into HTML (using a library like Parsedown) and assigned to $page['content'].
Example Routes
| URL | Resolved File |
|---|---|
/ |
pages/index.php |
/about |
pages/about.php or pages/about/index.php |
/docs |
pages/docs.php or pages/docs/index.php |
/docs/getting-started |
pages/docs/getting-started.php or pages/docs/getting-started/index.php |
/blog/my-post |
pages/blog/my-post.php or pages/blog/my-post/index.php |
/ABOUT |
pages/about.php (lowercased during sanitization) |
/about?ref=home |
pages/about.php (query params stripped) |
/does-not-exist |
404 (no matching file) |