Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Symfony Static Dumper Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Getting Started

symplify/symfony-static-dumper is a dev-only console tool that boots your Symfony kernel, iterates the router's GET routes, renders each one through the HTTP kernel, and writes the responses plus a configured set of public/ directories into a static output/ folder. Day-to-day, it is the "build step" for a brochure site, docs site, or blog — not for anything with a login or a POST.

Minimal setup:

composer require symplify/symfony-static-dumper --dev
// config/bundles.php
return [
    // ...
    Symplify\SymfonyStaticDumper\SymfonyStaticDumperBundle::class => ['dev' => true],
];

Then:

php bin/console dump-static-website -v
ls output/            # about/index.html, index.html, assets/...

Look first at three things in the vendor directory (the package is deprecated, so the source is the documentation):

  1. src/Command/ — the console command name, options and verbosity output.
  2. src/Config/ — the StaticDumperConfig class; this is your extension point for asset directories and route skipping.
  3. README.md at the tag you pinned — option names drifted across versions.

First use case to prove it works: a two-route app (/ and /about) with no database and no forms. Get output/index.html and output/about/index.html produced, serve output/ with php -S localhost:8000 -t output, and only then add real content. Doing it in that order separates "my config is wrong" from "my app has a hidden dependency."

Implementation Patterns

Wire it as a build artifact, not a runtime concern. Keep the package in require-dev, run the dump in a CI job that has dev dependencies and a seeded database, and publish only output/. The Symfony app itself is not deployed.

# .github/workflows/static.yml (sketch)
- run: composer install
- run: php bin/console assets:install
- run: yarn build            # Encore must run BEFORE dumping
- run: php bin/console doctrine:fixtures:load -n
- run: php bin/console dump-static-website -v
- uses: actions/upload-pages-artifact@v3
  with: { path: output }

Configure assets by explicit copy, not by magic. The dumper does not run Encore or resolve the manifest; it copies directories you declare. Extend the config class:

namespace App\StaticDumper;

use Symplify\SymfonyStaticDumper\Config\StaticDumperConfig;

final class AppStaticDumperConfig extends StaticDumperConfig
{
    public function getStaticDirectories(): array
    {
        return [
            __DIR__ . '/../../public/build'  => 'build',
            __DIR__ . '/../../public/images' => 'images',
            __DIR__ . '/../../public/favicon.ico' => 'favicon.ico',
        ];
    }

    public function getRoutesToSkip(): array
    {
        return ['admin_dashboard', 'contact_submit', 'webhook_stripe'];
    }
}

Register it over the package's default (service alias or DI override in config/services.yaml) and confirm with php bin/console debug:container StaticDumperConfig that yours wins.

Skip-list triage as the primary workflow. On a real app, run once, collect every route that errors out or produces nonsense, and move them into getRoutesToSkip(). Typical additions: admin areas, anything requiring a session, error/preview routes, JSON APIs consumed by JS.

Route parameters. Routes with required placeholders generally can't be auto-dumped. Either enumerate the concrete URLs by adding thin static routes, or pre-generate a sitemap-style list and hit those paths. If the package version supports parameter seeds, prefer that over duplicating routes — duplicated routes silently drift from the real ones.

Serve the output with a redirect-aware server config, because the dumper writes path/index.html, not path.html:

location / {
    try_files $uri $uri/ $uri/index.html =404;
}

Use it for PR previews. Because it's just a command, a per-PR job that dumps and uploads to a preview URL gives reviewers a real, crawlable site — cheap and surprisingly high-leverage for marketing/CMS work.

Gotchas and Tips

  • It is deprecated. The repo lives under deprecated-packages/, has no dependents, and gets no fixes. Pin the exact version ("symplify/symfony-static-dumper": "x.y.z"), vendor-lock it, and treat the alternative (Stenope, or a wget --mirror / Panther-based crawler of a running container) as the exit plan when a PHP or Symfony upgrade breaks it.
  • No JavaScript execution. This is a kernel-level renderer, not a headless browser. Anything hydrated client-side will be an empty shell in the output, and XHR/fetch endpoints are never visited. If your app is SPA-ish, this tool is the wrong shape.
  • Absolute URLs break. app.request.host, url() in emails, canonical tags and og:image will all be localhost. Set router.request_context.host / scheme in the dump environment (or use default_uri under framework.router), and prefer relative asset paths in templates.
  • Stale CSRF tokens and forms. Every rendered form bakes in a token tied to a session that will not exist in production. Either point forms at an external endpoint (Formspree, a Lambda) or drop them; a "working" static form that silently 419s is the classic post-launch bug here.
  • Kernel state leaks across requests. Hundreds of handle() calls in one PHP process means the Doctrine identity map grows and any service caching per-request data (locale, current user, breadcrumbs) can bleed into later pages. If page 40 looks like page 3, that's this. Watch memory and consider chunking the route list across several invocations.
  • Non-deterministic output ruins diffs. {{ 'now'|date }} in a footer, random featured posts, or ad-slot rotation makes every build a 400-file diff. Freeze the clock (ClockMock, a fixed APP_BUILD_TIME env) and sort any "random" collections by a stable key.
  • Debug with a one-route run. Temporarily return every route name except yours from getRoutesToSkip(), run with -v, and inspect the single file. Much faster than reading a 300-line failure tail. Also assert on status code — a rendered 500 page is still written to disk and looks like success.
  • Build order matters. encore production, assets:install, then the dump. Dumping before building ships build/entrypoints.json referencing hashed files that don't exist yet, and the copy step will silently copy nothing.
  • Check redirects and trailing slashes explicitly. A route that 301s to /foo/ while your file is foo/index.html is fine on nginx with try_files and broken on S3 without an index-suffix rule. Test the real host, not php -S.
  • Extension points beyond config: the dumper itself is a normal service, so decorating it is the supported-feeling way to post-process HTML (rewrite absolute URLs to relative, inject analytics, strip X-Debug-Token comments). Prefer a small decorator over editing vendor code — you will need to upgrade eventually.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings