Weft
A lightweight PHP 8.4 engine for quickly launching simple, content-first websites.
Convention routing, SEO, optional i18n, caching, request tracing and form protection out of the box, for landing pages, portfolios, product and documentation sites. Deliberately not a full-stack framework: no ORM, no DI container, no admin panel.
# the filesystem is the route table
pages/
├── index.php → /
├── about.php → /about
└── notes/
├── index.php → /notes
└── [slug].php → /notes/why-weft
api/
└── contact.php → /api/contact # JSON
# generated by the engine, no extra work
/sitemap.xml · /robots.txt
Why it exists
Every small site re-solves the same problems
Clean URLs. Locale prefixes. A correct head section. A sitemap. Spam-resistant forms. Tracing. Asset hashing. None of this is the reason the site exists, and all of it has to work before the site can launch.
Weft puts that entire layer behind a small convention-driven core, so a new site is mostly content. You write pages and a little business logic; the engine handles the plumbing that is identical from one content site to the next.
It is built for developers launching landing pages, portfolios, product and documentation sites: the class of site where a full framework is more machinery than the problem deserves.
Boundaries
What Weft is not
It is not a full-stack framework: there is no ORM, only a thin PDO wrapper for the little querying a content site does.
It has no DI container: static facades are the intended design, not a shortcut waiting to be fixed.
It is not a CMS: there is no admin panel. Weft is an engine; your content lives in files under version control.
Core concept
A page file is the whole route
There is no route table and there are no controllers. The filesystem already encodes the structure of a content site. Weft reads it instead of asking you to repeat it.
pages/about.php serves /about. A directory index serves the directory's URL. A [slug] file serves the dynamic segment. Files under api/ serve JSON endpoints under /api/*. Adding a route means adding a file.
The head section is rendered from config with per-page overrides: title templates, canonical, Open Graph, Twitter cards, hreflang and JSON-LD. /sitemap.xml and /robots.txt are generated by the engine.
Every request also produces one JSONL trace timeline via Traceloom, with sensitive keys (password, token, api_key, …) redacted automatically.
Design rule
Everything optional is a flag, and off is a safe no-op
Optionality lives inside the engine, not in conditional code at your call sites. A feature you disabled behaves like a feature that was never there.
i18n is off by default. When you enable it, the default locale stays prefix-free (/about) and other locales get URL prefixes (/ru/about) with automatic hreflang. Until then, nothing about locales leaks into your pages.
The same rule holds everywhere: your page code calls the engine the same way whether a feature is on or off. Turning caching or captcha off never means hunting for the call sites that assumed it was on.
Disabled features stay safe
What "off" actually does
Caching off: Cache::remember() still executes the callback through a null store.
reCAPTCHA off: verification passes safely instead of blocking the form.
i18n off: one locale, no prefixes, no hreflang, no ceremony.
Alerting: best-effort by design; a failed push to AlertLoop never blocks a request.
What's in the engine
The bar for a feature: most content sites need it
Everything below is implemented and in v0.1.1. There is no separate list of half-built features: partial and experimental lists are empty.
01
Convention routing
pages/about.php serves /about, api/contact.php serves /api/contact, [slug] files serve dynamic segments. No route table, no controllers.
02
SEO under the hood
Title templates, canonical, Open Graph, Twitter cards, hreflang and JSON-LD rendered from config with per-page overrides. Generated /sitemap.xml and /robots.txt.
03
Optional i18n
Off by default. Enabled, the default locale stays prefix-free while others get prefixes, with automatic hreflang and canonical redirects.
04
Optional caching
File or Redis store. Cache::remember() is safe even when caching is off: the callback runs through a null store.
05
Request tracing
One JSONL timeline per request via golovanov/traceloom, with automatic redaction of sensitive keys.
06
Form protection by default
Rate limiting, CSRF tokens, a honeypot check and optional Google reCAPTCHA v3 that passes safely when disabled.
07
Alerting for sensitive spots
Push events to AlertLoop. Best-effort by design: alerting never blocks a request.
08
Batteries in the engine
HTTP client, thin PDO wrapper, JWT via firebase/php-jwt, security headers. The only two package dependencies are firebase/php-jwt and golovanov/traceloom.
09
Vite integration
Dev server with HMR in development; hashed production assets resolved from the Vite manifest.
Documentation
The example site is the documentation
The example/ directory is a complete working site that documents Weft while being built with it. Every documentation page you read is served by the engine it describes: the routing conventions, the SEO output, the forms on it are the live demonstration of their own chapters.
If a described feature broke, the site describing it would break with it. A separate docs site cannot make that guarantee.
Requirements
What you need to run it
PHP ≥ 8.4 with ext-curl and ext-json.
ext-pdo only if you use the database wrapper; ext-redis only for the Redis cache and rate-limit stores.
Distributed as the Composer library golovanov/weft, MIT license.
My role
What I own on this project
Product definition, architecture, implementation direction, technical review and release decisions.
Weft also connects my other tools into one stack: request tracing comes from Traceloom and alerting goes to AlertLoop. Building the engine that consumes them is how those libraries earn their keep.
Current status
Current state of the project
v0.1.1 released on 2026-07-22 as an alpha. The source is public on GitHub under MIT, with a 160-test PHPUnit suite and GitHub Actions CI from the first tag.
This site runs on Weft: golovanov.dev is the engine's first production consumer. Pre-1.0, the API may change between minor versions.
Deliberately out of scope
Rejected directions, not missing features
These three are recorded in the roadmap as decisions. Nothing beyond the released engine is on a promised roadmap today.
No
ORM
A content site barely queries anything. Weft ships a thin PDO wrapper and stops there. If your data layer needs an ORM, you need a framework, not this engine.
No
DI container
Static facades are the intended design. For a site of a handful of pages, container indirection costs more in ceremony than it returns in flexibility.
No
Admin panel
Weft is an engine, not a CMS. Content is files in a repository, edited with the tools you already use for code.
Install it, add one page file, load the URL.
That is the fastest way to judge the routing convention. If your site needs more machinery than that, Weft is not the right tool: it was built to stay small, and it will.