Guides
SERIXA apps are plain PHP: a front controller in public/ that renders HTML and returns it. Anything that can run PHP 8.2+ and serve a folder can host one. This page covers local serving, a real web server, and the CSS story for production.
public/Every generated app puts index.php, app.css, and assets/runtime/serixa.js under public/. Your web server's document root must point at that folder — not the app root — or the runtime script and stylesheet will 404.
blog/
public/ ← document root goes here
index.php
app.css
assets/runtime/serixa.js
pages/
layouts/
vendor/ ← keep this OUTSIDE the document rootphp -Sphp -S localhost:8000 -t public-t public sets the document root. This is what composer install sets up as the start script in generated apps:
composer startGood for development; PHP's built-in server is single-threaded and not meant for production traffic.
Point the vhost's DocumentRoot at public/, and route unmatched requests to index.php (SERIXA apps read $_GET['page']-style routing, so you generally don't need a catch-all rewrite unless your app uses path-based URLs):
<VirtualHost *:80>
ServerName blog.example.com
DocumentRoot /var/www/blog/public
<Directory /var/www/blog/public>
AllowOverride All
Require all granted
</Directory>
</VirtualHost>If you want clean paths (/articles instead of /?page=articles), add a public/.htaccess that rewrites everything to index.php and read $_SERVER['PATH_INFO'] (or similar) in your own routing code — SERIXA has no opinion here, since it ships no router.
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]server {
listen 80;
server_name blog.example.com;
root /var/www/blog/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location ~ /\.(?!well-known) {
deny all;
}
}Adjust the fastcgi_pass socket/port to match your PHP-FPM setup.
ClientRuntime::register() writes a <script defer src="..."> tag pointing at whatever URL you pass it — by convention /assets/runtime/serixa.js:
ClientRuntime::register($document->assets(), '/assets/runtime/serixa.js');That URL has to resolve to an actual file. serixa new copies the shipped script into public/assets/runtime/serixa.js automatically. If you move, rename, or proxy your app, make sure that file still exists at the URL you registered — a mismatch is a common cause of a 404 for the runtime script with no visible errors (interactive widgets like Modal or Dropdown will render but simply not respond to clicks). See troubleshooting.md.
To point at a different path (e.g. serving assets from a CDN or a versioned folder), just register a different URL and copy the file there yourself:
ClientRuntime::register($document->assets(), 'https://cdn.example.com/serixa/0.11.0/serixa.js');The canonical source file is always Serixa\Runtime\ClientRuntime::scriptPath() (physically assets/runtime/serixa.js in the framework package).
SERIXA apps register the compiled stylesheet — never a Tailwind CDN:
use Serixa\Assets\StyleSheet;
use Serixa\Runtime\ClientRuntime;
StyleSheet::register($document->assets()); // /assets/serixa.css
ClientRuntime::register($document->assets());Build assets before deploy (offline):
vendor/bin/serixa buildThis merges framework/assets/css/serixa.css with resources/css/app.css into public/assets/serixa.css, copies the runtime JS, and writes public/assets/manifest.json.
See assets.md for the full pipeline. Maintainers regenerating the base stylesheet use tools/css (Node once); shipped apps stay offline.
For production, deploy public/assets/serixa.css and public/assets/runtime/serixa.js with your app. Do not register cdn.tailwindcss.com.
If your app's composer.json uses a local path repository (common right after serixa new, while developing next to a framework clone), switch to a real version constraint before deploying:
{
"require": {
"serixa/framework": "^0.11"
}
}and remove the repositories block. Path repositories with "symlink": true are a local development convenience; they don't make sense once the app is deployed independently. See Path-repository recursion for the related monorepo warning.
public/, not the app root.public/assets/serixa.css exists (from serixa build) and is registered via StyleSheet::register()public/assets/runtime/serixa.js exists and matches the URL passed to ClientRuntime::register()composer.json uses a version constraint for serixa/framework, not a local path repository.composer install --no-dev --optimize-autoloader run on the deployment target.