symplify/symfony-static-dumper
Symfony Static Dumper converts a Symfony application into a static HTML site. It crawls configured routes, renders pages, and writes them to disk, enabling fast, low-maintenance static deployments while preserving Symfony routing and templates.
What the package actually does. symplify/symfony-static-dumper is a CLI tool that boots a Symfony kernel in-process, enumerates the application's routes (or walks an internal crawler from a seed URL), dispatches a synthetic Request for each one, and writes the resulting Response bodies to a target directory as static files. It layers on asset copying and a configurable output path so the result can be served from a CDN or static host. Conceptually it is "Symfony app → folder of HTML," not a templating or content-pipeline tool.
Architecture fit. Fit is narrow and conditions are strict. It only works when every meaningful page is a GET, renders deterministically, and is identical for every visitor. Any of the following disqualifies the app outright: authenticated or session-scoped content, CSRF-protected forms, POST-driven flows, personalized recommendations, locale/cookie negotiation, rate-limited or third-party-calling controllers, or anything that mutates state on render. Because the dumper executes your real kernel against a synthetic request, all of your runtime dependencies — database, cache, HTTP clients, mailer, queue — are live during the dump. That is a substantial operational coupling that a naive reading of "static export" does not imply.
Integration feasibility. Mechanically simple: a Composer dev dependency, a config file defining output directory, base URL, and route inclusion/exclusion, and a single console command. The real cost is not installation but proving the dump is faithful — route coverage, query-string variants, trailing slashes, generated URLs inside content that point at the live domain rather than the static host, and cache headers that no longer apply when files are served from S3 or Netlify. Expect a nontrivial audit phase rather than a drop-in switch.
Technical risk — this is the dominant finding. The repository lives under deprecated-packages/, the score is 0, stars are unrecorded, and dependents are 0. Three signals converge: the maintainer has explicitly retired it, there is no downstream production usage to learn from, and there is no community to absorb the maintenance burden. It almost certainly targets Symfony 4/5-era HttpKernel and Router internals (route collection iteration, Request::create semantics, KernelInterface::handle) that have shifted in Symfony 6/7. Pin-and-pray works only inside a matching legacy stack; the moment the host application upgrades Symfony, this package is the thing that blocks the upgrade or gets silently deleted. There is also a security dimension: the dumper renders with production credentials against a live datastore, so a misconfigured inclusion rule can publish internal or draft content to a public bucket. Treat that as a release-process risk requiring a hard gate, not a config detail.
Key questions I would put to the team before any pilot.
HttpCache/http-client warmup, a headless CMS export, or a first-party static generator — that covers the same need?Stack fit. Reasonable only for a conventional Symfony monolith with server-rendered Twig templates, a stable public route set, and a build pipeline that already produces a deployable artifact (CI, container image, or release tarball). It fits poorly alongside API-first or SPA front ends, where the static output adds nothing the client bundle does not already produce, and poorly alongside multi-tenant or per-user rendering.
Migration path. Frame this as an extraction, not a migration in the usual sense. Sequence: (1) inventory all public routes and classify each as static-safe, needs-parameterization, or must-exclude; (2) stand up a disposable, seeded, read-only database snapshot for the dumper to run against — never production; (3) run the dumper to a scratch directory and diff route coverage against the inventory; (4) validate output in a local static server, checking internal links, canonical URLs, absolute asset paths, and any domain hardcoded in content; (5) only then wire it into CI and add a content-safety gate. If the goal is decoupling from a running server, the honest alternative is to keep Symfony serving the pages behind a CDN and skip the dumper entirely — many teams adopt this package to solve a caching problem they do not actually have.
Compatibility. Two axes. Vertically, with the framework: verify against the exact Symfony minor version in composer.lock, not the constraint range; the package's reflection and router interactions are the fragile part. Horizontally, with the deployment target: static hosts impose their own redirect, header, and 404 semantics that the dumper does not model, so those must be reimplemented in host configuration. Plan for the package to constrain the framework upgrade path, and record that constraint in the roadmap explicitly so it is a known trade-off rather than a surprise.
Sequencing. Do the route inventory and the content-safety review before writing any integration code — they are the cheapest possible disqualifiers. Then a single-app pilot behind a feature flag or a separate CI job, one publish cycle observed end to end, then decide on fork-vs-wrap. If the decision is "adopt ongoing," budget for a fork in the same quarter; do not defer it, because the upstream is already gone.
Maintenance. The realistic assumption is that we own the code from day one. The upstream is archived by its author, so "maintenance" means forking, vendoring, or accepting a frozen dependency with a known expiry date tied to our next Symfony upgrade. Budget a small but permanent line item: someone must re-validate the dumper on every framework and PHP bump. If nobody will own that, the correct decision is not to adopt.
Support. No vendor, no upstream issue triage, no security advisories, no community. Internal knowledge will be concentrated in whoever wrote the integration — a bus-factor of one unless deliberately spread. Any production incident in the publish pipeline is ours alone to diagnose, with no upstream changelog to consult.
Scaling. Two dimensions, both benign and both constrained. Output volume scales linearly with page count and page size, so a few thousand pages is trivial and tens of thousands becomes a storage and CI-artifact question, not a runtime one. Dump duration scales with the number of routes times per-request latency, because requests are processed serially in-process — a slow controller makes the build slow, and that cost is paid on every publish. Mitigate by excluding low-value routes and by accepting that incremental dumps are not a feature the package offers; every publish is a full rebuild.
Failure modes to design against.
Ramp-up. Low for the mechanics — a competent Symfony developer can produce a working dump in an afternoon. High for the judgment: knowing which routes are legitimately static, designing the safety gate, and accepting ownership of a dead dependency are the hard parts. Estimate one engineer-week for a credible pilot including the route inventory, safety review, and CI gate; multiply by the number of distinct rendering contexts (locales, tenant variants) if the app has any. The recommendation for a TPM: approve only as a time-boxed, single-purpose export with an explicit fork-and-own commitment and a sunset date tied to the next framework upgrade; reject as a long-lived strategic dependency.
How can I help you explore Laravel packages today?