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

Prerender Bundle Laravel Package

setono/prerender-bundle

Symfony bundle that serves prerendered HTML to search engine crawlers and social bots, improving SEO and link previews for JavaScript-heavy apps. Works with prerender.io or a self-hosted service, with configurable user-agent and middleware rules.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install the bundle with Composer:

    composer require setono/prerender-bundle:^1.0@alpha
    

    Note the explicit stability flag — the only published releases are 1.0.0-alpha*, so a plain ^1.0 will not resolve without minimum-stability changes.

  2. Register the bundle (Symfony 5.4+ usually does this via Flex; otherwise add it to config/bundles.php):

    Setono\PrerenderBundle\SetonoPrerenderBundle::class => ['all' => true],
    
  3. Add the minimum configuration. Point url at your prerender endpoint — either a hosted service (prerender.io, https://service.prerender.io/) or a self-hosted Rendertron instance (http://rendertron:3000/render/):

    # config/packages/setono_prerender.yaml
    setono_prerender:
        url: '%env(PRERENDER_URL)%'
        token: '%env(PRERENDER_TOKEN)%' # only needed for prerender.io
    
    PRERENDER_URL=https://service.prerender.io/
    PRERENDER_TOKEN=your-token
    
  4. Where to look first: open vendor/setono/prerender-bundle/src/ and read the kernel event listener/subscriber — that is the entire "engine" of the package. Look specifically for where it reads and writes the _prerender request attribute; that attribute is the package's main public seam. Then check DependencyInjection/Configuration.php to see the exact config keys your installed version supports (the package is small and low-adoption, so the source is the docs).

  5. First validation: hit one of your routes with a bot user agent and confirm the response is the prerendered HTML, not your SPA shell:

    curl -A "Googlebot/2.1 (+http://www.google.com/bot.html)" https://your-site.test/products/42 -I
    curl -A "Mozilla/5.0" https://your-site.test/products/42 -I
    

    The bot request should be served from the prerender service (look for a different Content-Length, Server header, or the service's X-Prerender headers), while the human request returns your normal app shell. If both return the shell, check the _prerender attribute handling first — that is exactly what 1.0.0-alpha.2 fixed.

Implementation Patterns

  • Bot-traffic interception. The bundle attaches a kernel.request listener that inspects the User-Agent, matches it against a crawler whitelist (Googlebot, Bingbot, Twitterbot, Slackbot, facebookexternalhit, etc.), and — if matched — proxies the request to ${url}${absolute_url} instead of running your controller. Your SPA routes stay untouched; only the request pipeline changes.

  • Use the _prerender request attribute as your control switch. The bundle marks its decision on the request via a _prerender attribute rather than acting purely on the UA inline. That gives you a clean, supported hook: set _prerender in a route default (or in your own listener running at a higher priority) to force prerendering on a route that the UA matcher would miss, or to suppress it on a route that shouldn't be proxied. Note that prior to 1.0.0-alpha.2 this attribute was handled incorrectly, so any logic you built against it on alpha/alpha.1 needs re-testing on upgrade.

  • Environment-scoped config. The bundle has no business being active on local dev (it would slow every request and hide real errors). Keep it enabled only for prod and a staging environment you can use for validation:

    # config/packages/prod/setono_prerender.yaml
    setono_prerender:
        url: '%env(PRERENDER_URL)%'
    
  • Whitelist the crawlers that actually matter to you. SEO-focused bots (Google, Bing) and social unfurlers (Slack, Twitter/X, Facebook, LinkedIn) are the ones that need rendered HTML. If the bundle exposes a crawler list in config, trim it — every extra bot in the list is a route by which a slow upstream can degrade your p95 latency.

  • Route allow-list / deny-list. Prefer serving prerender only for GET requests to public, crawlable routes. Skip /admin, /api, /health, and anything requiring authentication — the prerender service will hit those anonymously and produce garbage or leak internals. Express this via route defaults (_prerender: false) rather than hard-coding paths in a decorated listener where you can avoid it.

  • Pair with fragment support. If you still support the legacy AJAX-crawling scheme (?_escaped_fragment_=key), map it back to the pretty URL (/#!/key → /key) before handing the request to the bundle, so the service renders the canonical page rather than the escaped fragment URL.

  • Cache aggressively on your edge. Prerendered responses are deterministic for a given URL — set Cache-Control: public, s-maxage=… on the proxy response, or use a Varnish/CDN rule keyed on URL + "is bot". The bundle itself does not cache (it proxies on every bot hit), so caching is your responsibility.

  • Custom prerender service. For a self-hosted Rendertron, point url at the /render/ endpoint and expect the bundle to concatenate it with the absolute URL. Verify with one curl that the format is correct (http://rendertron:3000/render/https://your-site/products/42) — this is the single most common misconfiguration.

  • Testing without real bots. Write a functional test that sends a request with a Googlebot user agent and asserts the response body matches the prerendered fixture, and a companion test with a browser user agent asserting the app shell. Add a third case that sets _prerender explicitly on the request and asserts your override wins — that pins down the behaviour the alpha.2 fix touched and protects you on the next bump.

Gotchas and Tips

  • This bundle does not render anything. It is a bot detector + HTTP proxy. If PRERENDER_URL is down, unreachable, or misconfigured, bot requests will error or hang. Budget for a timeout and fall back to serving the SPA shell — an un-prerendered page is strictly better than a 500 to Googlebot (which will de-index the URL).

  • Infinite loop risk with self-hosted Rendertron. If the prerender service fetches your site and that fetch does not carry a non-bot User-Agent, the bundle will proxy it again. Rendertron by default strips the Googlebot UA, but confirm your setup doesn't reintroduce it. A X-Prerender-Request: 1 header on the outbound call (checked by the listener) is the usual escape hatch.

  • User-Agent sniffing is spoofable and imperfect. Any client can claim to be Googlebot, and some real crawlers (e.g. newer AI training bots) will not appear in the bundled list. Don't treat "matched a bot UA" as a security boundary — it is a rendering optimization only.

  • The token config is service-specific. A prerender.io token sent to a Rendertron endpoint (or vice versa) is either ignored or yields a 401. Keep the token in a per-environment secret, and never commit it — the bundle forwards it as a header and it will show up in logs if you log the outgoing request.

  • Trailing slash in url. https://service.prerender.io/ + /products becomes …/prerender.io//products if the joiner is naïve. Always end the configured URL with a single / and let the bundle concatenate the absolute URL; test the exact string sent to the service once, then forget it.

  • Extension point is the listener plus the _prerender attribute. If you need to skip specific requests, add per-route logic, or override the crawler list dynamically, prefer setting _prerender on the request; failing that, decorate or subclass the bundle's request listener rather than fighting the config tree. The config tree in this package is minimal, so most customisations live in code.

  • Versioning: this is still pre-stable. Every tagged release so far is a 1.0.0-alpha*, and alpha.2 is a bugfix to attribute handling — i.e. the public seam changed meaning between alphas. Pin the exact version ("setono/prerender-bundle": "1.0.0-alpha.2" with a committed composer.lock), read the diff before bumping, and be prepared to fork the listener if a Symfony 7/8 kernel event change breaks it. The logic is a few dozen lines — forking is cheap and often the cleaner long-term bet than depending on an unmaintained proxy layer.

  • Maintenance signal. Zero dependents, one star, and a last substantive release in April 2023 mean the package is effectively frozen; alpha.2 is a one-line-class fix, not a sign of renewed activity. Treat the source as read-only reference and keep your fork path short.

  • Debugging tip: log the resolved prerender URL, the matched UA, and the final value of the _prerender attribute at debug level on the listener. Without that, a "wrong page served to bots" ticket is nearly impossible to diagnose because the misbehaviour is invisible from a browser — and attribute-propagation bugs (the class fixed in alpha.2) look identical to config errors from the outside.

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.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core