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

Sitemap Bundle Laravel Package

jgniecki/sitemap-bundle

jgniecki/sitemap-bundle is a Symfony bundle for generating XML sitemaps from your application's routes and content. Configure sitemap definitions, collect URLs automatically, and output search-engine-friendly XML to simplify SEO setup.

View on GitHub
Deep Wiki
Context7

Getting Started

First, verify what you actually have. jgniecki/sitemap-bundle is a Symfony bundle, not a Laravel package. Check composer.json in the repo: you will almost certainly see "type": "symfony-bundle" and symfony/framework-bundle in require. That has three consequences a Laravel developer must internalise before spending any time on it:

  • There is no ServiceProvider, no Facade, no config/sitemap.php to publish, and no php artisan vendor:publish.
  • It is registered through config/bundles.php in a Symfony kernel, not through Laravel's package discovery.
  • Composer will happily download it into vendor/, but nothing in a Laravel app will ever boot it.

Where to look first (in this order):

  1. composer.json — PHP constraint, Symfony version constraint, and whether type is really symfony-bundle. This single file tells you whether it can run in the Symfony 7.x / PHP 8.3 project you or a teammate maintains.
  2. README.md — look for the phrase "register the bundle" and for any console command name (bin/console ...:sitemap:...). If the README only shows YAML config and no Laravel-style setup, your decision is made.
  3. src/ — look for a *Bundle.php class, Command/, Controller/, and Resources/config/services.yaml. Those four locations define the entire surface area of a bundle this size.
  4. Releases/tags — v1.1.0 landed 2025-09-04 with host-specific sitemap groups, host-level sitemap paths, and route filtering by host. It is maintained and actively gaining features, but with 0 stars and 0 dependents you still have no community signal about whether it works.

The shape of the package has changed in v1.1.0. It is no longer purely "one sitemap from your routes." It now models sitemap groups scoped to hosts, with per-host output paths and host-aware route filtering. If you are single-domain, none of this affects you and the old mental model holds. If you are multi-domain or multi-locale-per-host, this is the first release where the bundle is a plausible fit at all — read the host/group config keys before anything else.

First use case (if you are in a Symfony app): register the bundle in config/bundles.php, define one host group in config/packages/<vendor>_sitemap.yaml, run the generation command, and dump the output to public/sitemap.xml. Explicitly configure a host even if you only have one — v1.1.0 emits warnings about the default group, and you do not want production deploys printing deprecation-style noise because you took the zero-config path. Treat that as a spike, not an install.

If you are writing Laravel code: do not adopt this. Use spatie/laravel-sitemap, which is the idiomatic solution and is designed for Route:: / Eloquent model crawling out of the box. The v1.1.0 host-group feature does not change this — multi-domain sitemaps in Laravel are still better solved natively. The only defensible reason to touch this package from Laravel is if you are bridging to an existing Symfony service, and even then the bridge (below) is the interesting part, not the bundle.

Implementation Patterns

Symfony-side integration (the only native path).

  • Registration and config. Add the bundle to config/bundles.php, then create config/packages/<vendor>_sitemap.yaml. Because this package almost certainly has no Symfony Flex recipe (0 dependents ⇒ no recipe submission), expect to write this config by hand and to read services.yaml in the bundle to discover the real config keys rather than trusting docs that may lag the code.
  • Console-driven generation. Sitemap bundles of this shape typically expose a console command that materialises XML. Wire it to cron or a scheduler entry, write to public/, and never generate on the request path — sitemap generation is a batch concern and will blow up your p95 if you put it in a controller.
  • Route/entity providers. The useful extension point is almost always "feed me URLs." Look for a tagged service interface (UrlProviderInterface or similar) or an event listener. Implementing your own provider with repository queries is where the real logic lives; keep it out of the bundle's classes so you can swap the bundle later.
  • Host-scoped groups (new in v1.1.0). The config now expresses sitemaps as groups keyed by host, each with its own output path. The pattern to adopt:
    • One group per public hostname (www.example.com, shop.example.com, example.fr). Do not try to cram multiple hosts into one group — the bundle filters routes by host per group, and mixing them produces sitemaps containing URLs for domains the file is not served from, which is a validation error at best and a Search Console penalty at worst.
    • Set the host-level path explicitly rather than relying on the default. v1.1.0 added the override precisely because the default collides across groups; two groups with default paths will overwrite each other's file.
    • Route filtering by host means the bundle inspects your route definitions' host constraints. Routes with no host constraint are the ambiguous case — decide deliberately whether they belong in every host group or none, and verify by reading the generated file. This is the single most likely source of "my sitemap is missing half my URLs" bugs.
  • Sitemap index for scale. If you exceed 50,000 URLs or 50 MB uncompressed (the protocol limit) — per group, not globally — you need a sitemap index + child sitemaps. Multi-host setups hit this sooner than you think because each host group counts separately. Verify the bundle supports an index before adoption; many small bundles don't, and retrofitting is expensive.
  • Deployment. Generate during deploy or in a queue worker, write to public/, invalidate the CDN, then ping search engines. Multi-host means N files and N CDN invalidations — script the loop, don't do it by hand. Do not commit generated files to git.

Cross-framework bridge (if you genuinely must serve Symfony-generated XML from Laravel).

  • Run the Symfony app (or a single Symfony console command in a container) as a build step, emit the sitemap files, and copy them into the Laravel app's public/. Laravel then just serves static files. This is boring, robust, and avoids loading Symfony's kernel inside a Laravel request lifecycle — which is where mixed-framework projects go to die.
  • v1.1.0 makes the bridge slightly more attractive for multi-domain estates, since host grouping and per-host paths are work you would otherwise hand-roll. But it also adds coupling: your Laravel deploy now depends on a Symfony build container whose config encodes hostname-to-path mappings that must stay in sync with Laravel's own domain routing. Keep that mapping in one shared file (env vars or a JSON manifest) consumed by both sides.
  • If you need dynamic content from the Laravel database, export the URL list (JSON/CSV) from Laravel, have the Symfony build step consume it, and produce the XML. Two small scripts that talk via files beat one large script that talks via two IoCs.
  • Alternative that keeps everything in Laravel: spatie/laravel-sitemap with a custom URL set fed by the same query, one sitemap per domain. For most teams this is strictly better than bridging, because the sitemap logic and the content both live in one codebase.

If you are evaluating rather than adopting, write a 30-minute spike: fresh Symfony app, composer require, register, configure two host groups with distinct paths, generate, view source, and confirm each file contains only its own host's URLs. Validate against xmllint --noout. If that path isn't clean, the package is not ready for production regardless of star count — and v1.1.0's whole value proposition is that path.

Gotchas and Tips

  • The framework mismatch is the headline gotcha. A Laravel developer cannot "just use" a Symfony bundle. If someone in your org proposed this for a Laravel project, the correct response is a one-line Slack message pointing at spatie/laravel-sitemap plus the composer.json type field as evidence. v1.1.0's new features do not move this needle.
  • v1.1.0 warns about the default group — treat that as a migration signal. The warnings exist because the implicit single-sitemap behaviour is on its way out. If you adopted v1.0.x with zero config, budget an hour to move to explicit host groups now, while the old behaviour still works, rather than when it is removed. Read the PR titles (#10, #11, #12) if the README hasn't caught up — three host-related PRs in one minor release tells you where the author's attention is going.
  • Host filtering can silently drop URLs. With route filtering by host, any route whose host constraint doesn't match a configured group is excluded. Dynamic routes registered without host constraints, or with a host constraint that doesn't exactly match your config string (example.com vs www.example.com), vanish from the output with no error. Diff your route list against the generated XML on the first run and again after every deploy that touches routing.
  • Per-host paths must be unique. v1.1.0 lets you set the sitemap path per host — a feature that exists because the default collides. Two groups writing to the same path is last-write-wins; you get one host's sitemap, silently, with no warning that the other was clobbered. Assert file count in CI (find public -name 'sitemap*.xml' | wc -l equals group count).
  • Zero stars, zero dependents, zero score. You are the first adopter. There is no Stack Overflow corpus, no GitHub issue history to search, no security advisories, and no upgrade guide. Every bug you hit is yours to debug in vendor/. Budget accordingly, and pin to an exact version ("1.1.0", not ^1.1) — a minor-version feature drop this large is exactly where you don't want unvetted drift on deploy.
  • Read the code, not the docs. The docs are almost certainly behind v1.1.0 — three PRs merged in a single release usually outpace a README update. Resources/config/services.yaml, the *Bundle.php class, and the config tree class (look for Configuration.php / TreeBuilder) are the ground truth for host/group keys and whether the "default group" warning is a deprecation or a validation error.
  • Debugging tools that actually help (Symfony): bin/console debug:container --tag=<tag> to find registered providers, bin/console debug:router to confirm any XML endpoint and to inspect the host column of your routes, bin/console list to find the generation command. debug:router is now doubly useful — it's the fastest way to explain a missing URL.
  • Absolute URLs and proxies. Sitemaps require absolute URLs with the correct scheme and host. Behind a load balancer or reverse proxy, bundles that build URLs from the request context will emit http:// and internal hostnames. Configure trusted_proxies / framework.trusted_hosts and verify the XML by eye. v1.1.0 makes this more dangerous, not less: host group membership may now be decided from the same context, so a misconfigured proxy can route URLs into the wrong group's file.
  • XML escaping and validation. Ampersands in query strings and non-ASCII in slugs must be escaped. Always validate the generated files with xmllint --noout in CI — loop over every host group's output. A malformed sitemap fails silently in Search Console and you may not notice for weeks.
  • Caching and stale output. If you cache the generated XML or the URL list, remember to bust it on content publish. With host groups, a content publish on one domain should not invalidate all N files, but a bad cache key will do exactly that — or worse, leave one stale. A sitemap that lags your content by a day is worse than no sitemap.
  • Gzip and content type. Serve as application/xml (or application/xml; charset=UTF-8), and if you gzip, name the file sitemap.xml.gz and reference it correctly. Don't let your web server serve it as text/plain. Per-host, per-file — check the vhost config for each domain, not just the primary one.
  • Extension points to inspect before adopting: a tagged URL-provider interface, an event dispatched before render, and a configurable base URL. Host groups partially answer the last one, but if there's no way to inject URLs programmatically you'll be forking on day two regardless.
  • Abandonment risk is real but bounded. This package is one composer.json and a handful of classes, still growing fast enough that its API is unstable. If you adopt it, vendor a copy or be prepared to fork; the cost of forking is an afternoon, and that should inform your build-vs-buy call far more than the star count.
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