piedweb/static-bundle
PiedWeb StaticBundle brings static site generation to Symfony apps, letting you export pages as pre-rendered HTML files for fast, cache-friendly delivery. Serve a whole site or selected routes statically while keeping your existing Symfony routing and Twig templates.
Before installing anything, be aware this bundle is archived and has near-zero adoption (1 star, 0 dependents). It is effectively the historical static-generation layer for Pied Web CMS on Symfony — not a general-purpose SSG. Treat it as "read the source before you composer require."
Minimal steps for an existing PiedWeb/Symfony project:
dev-main maintenance. Add piedweb/static-bundle to composer.json with an explicit constraint that matches the Symfony version your CMS already runs. Do not let Composer resolve "latest."config/bundles.php (Symfony Flex style) or app/AppKernel.php (classic style, depending on the Symfony version the bundle targets):
PiedWeb\StaticBundle\PiedWebStaticBundle::class => ['all' => true],
src/Command/. The bundle's entire value is a console command that walks configured routes/entities and renders them to disk. Open that file before touching YAML — it tells you what the actual config keys are, because the README on an archived 1-star repo is frequently stale.bin/console piedweb:static:generate or piedweb:static:build):
bin/console list | grep -i static
bin/console piedweb:static:generate -vvv
index.html files with correct asset references before wiring it into any deployment.First use case: a PiedWeb CMS site where the marketing/blog pages should be served as flat HTML (Cloudflare Pages, Netlify, S3+CloudFront, GitHub Pages) while /admin remains a live Symfony app on the origin. Generate, rsync or CI-publish the output, and only route public traffic to the static host.
Console command + scheduled rebuild. The realistic operating model is cron or CI, not request-time generation:
# .github/workflows/deploy.yml (sketch)
- run: composer install --no-dev
- run: bin/console cache:warmup --env=prod
- run: bin/console piedweb:static:generate
- run: aws s3 sync var/static/ s3://my-site/ --delete
Run the generator inside the same environment that renders the live site, or you get Twig/asset divergence.
Config-driven page selection. The usual shape for these bundles is a list of routes or entity IDs to render. Typical config sits in config/packages/pied_web_static.yaml:
pied_web_static:
output_dir: '%kernel.project_dir%/var/static'
routes:
- { name: homepage }
- { name: article, params: [slug] } # enumerate slugs from repo
Because the package is archived, assume the enumeration logic is your job: query the Article (or whatever the CMS entity is) repository and hand the generator a list of parameters. Do not expect the bundle to auto-discover your whole sitemap unless you can read that in src/.
Twig template parity. Static generation re-renders your existing templates with a Symfony kernel, so your Twig must not depend on app.request state you don't set. If a template does {{ app.request.host }}, either inject the host via config or set a dummy request — otherwise baked-in links point at localhost.
Hybrid hosting. Standard pattern: static host serves /, /blog/*, /about; the origin (origin.example.com) serves /admin, form POSTs, search, and preview URLs. Route via reverse-proxy rules (Cloudflare Workers, Netlify redirects, ALB rules). Keep the admin path off the static host entirely.
Asset handling stays separate. Do not expect the bundle to compile Webpack Encore entries. Run yarn encore production first, then generate, then publish both public/build/ and the generated HTML. If the bundle rewrites <link> tags, verify the rewritten hashes match what's on disk.
Sitemap and feeds. Generate sitemap.xml and RSS in the same step as HTML so the static host has no live endpoints to fall back on. These are almost certainly not in the bundle — write a small SitemapGenerator service and call it from your own command that wraps the bundle's generator.
Archived means no security patches. Every composer update risks a silent break. Keep it behind a composer.lock and consider forking into app/ if you need a single fix. For new projects, evaluate spatie/laravel-... equivalents less than something like Symfony's static extensions or a generic crawler (e.g. wget --mirror on a staging instance) — this bundle's value is small enough that a fork or a 40-line custom command is often cheaper.
Symfony version lock. A bundle this small almost certainly pins to Symfony 4/5-era APIs (KernelInterface, EventDispatcherInterface deprecations). Test the command on your exact Symfony LTS before assuming compatibility; the archived status means no ^7.x bumps.
Absolute URL baking. The most common production failure: pages render with http://localhost:8000 canonical tags, og:url, and internal links. Set APP_URL / router.request_context.host in config/packages/routing.yaml or the equivalent before running the generator:
framework:
router:
default_uri: 'https://example.com/'
Then bin/console cache:clear and regenerate.
Query-param routes are invisible to the generator. If an article lives at /article?slug=foo, the generator may or may not enumerate it. Check the command's source for whether it supports {slug} placeholders; if not, loop in a wrapper command and generate one URL per call (slow but correct).
Trailing-slash and path collisions. Flat files (/about/index.html) vs. extensionless (/about.html) differ per host. Match the bundle's output convention to your CDN's rewrite rules, or /about 404s on Netlify while working on S3.
Dynamic features break silently. Contact forms, search, pagination with ?page=2, and CSRF-protected POSTs will 200 on first render and then fail on submit against the static host. Audit every template for <form> and path() calls to dynamic-only routes; replace forms with a hosted endpoint (Formspree etc.) or point them at the origin.
Debugging the generator. Because the repo has no maintained issue tracker, learn to read the output yourself:
-vvv and inspect the var/log/ entries — the command likely logs each URL it renders.var/static/index.html against the live response (curl -s localhost:8000/ | diff - var/static/index.html); the diff usually reveals missing request context or asset rewrites.pied_web_static.routes or an empty repository query.Extension points. Symfony's DI container is your escape hatch when the bundle is archived: override the generator service in config/services.yaml with decorates: to add logging, post-processing (minify HTML, inject CSP headers into <meta>), or to swap in your own URL enumerator. This is far safer than patching vendor code.
How can I help you explore Laravel packages today?