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

Elasticsearch Proxy Bundle Laravel Package

xola/elasticsearch-proxy-bundle

A Symfony bundle that adds a proxy layer in front of Elasticsearch, simplifying index and query handling for PHP applications. It wraps client access behind bundle configuration so services can search and index without direct Elasticsearch wiring.

View on GitHub
Deep Wiki
Context7

Getting Started

The first thing to internalize: this is a Symfony bundle, not a Laravel package. There is no service provider, no config/elasticsearch-proxy.php, and composer require xola/elasticsearch-proxy-bundle in a Laravel app will pull in symfony/framework-bundle, symfony/http-kernel, and friends without doing anything useful. Treat it as a reference architecture + deployable sidecar, not a dependency.

Two viable paths, in order of effort:

  1. Run it as a sidecar (recommended). Clone the repo, containerize the Symfony app, point it at your Elasticsearch cluster, and have Laravel talk to it instead of ES. Your config/elasticsearch.php becomes:

    'hosts' => [env('ELASTICSEARCH_PROXY_URL')],
    

    and ES itself is never reachable from your app network. This is how you get the authorization guarantees without porting anything.

  2. Mine it for the authorization logic. Read the request listener / controller that intercepts _search calls and the voter (or equivalent authorization service) that decides what a principal may see. Re-implement the query-rewriting in a Laravel middleware or a decorator around Elasticsearch\Client. Expect to rewrite, not port — the DI wiring won't transfer.

Where to look first: the bundle's Resources/config/services.* for the wiring graph, then the proxy controller — that's the whole product in one class. Then the security/voter layer for the rules you'll want to mirror.

First use case: multi-tenant index access. A SaaS app where every document carries a company_id / tenant_id, and you cannot trust the client to filter by it. The proxy stamps the filter on the way through, so a malicious or buggy query still can't cross tenants.

Implementation Patterns

Sidecar + signed context header. Laravel authenticates the user, mints a short-lived HMAC or JWT containing tenant_id and allowed index patterns, and attaches it as a header. The proxy validates the signature, discards any client-supplied tenant filter, and injects its own. Never let the proxy trust a plain X-Tenant-Id header — it must be signed or the whole exercise is theater.

A thin client wrapper in Laravel. Rather than sprinkling the official client around, bind a single SearchGateway:

$this->app->bind(SearchGateway::class, fn () => new SearchGateway(
    ClientBuilder::create()->setHosts([config('elasticsearch.proxy')])->build()
));

Give it search(), index(), bulk() methods with typed params. When you later swap the proxy for ES's native security features, you change one class.

Query-time rewriting over per-tenant indices. Two schools: (a) one index with a mandatory term filter per request, or (b) index-per-tenant with the proxy rewriting the _index path. (b) is cleaner for deletes and mappings but multiplies shard count; (a) is what most teams land on. The proxy approach supports either — pick before you write the Laravel side.

Filtered aliases as a cheap alternative. If your needs are coarse (index-level, not document-level), filtered aliases plus one alias per tenant gives you most of the isolation with zero proxy code. Reach for the proxy only when you need per-document or per-field rules.

Middleware for context propagation. Stamp the tenant/auth header in a global middleware so queued jobs and console commands use the same path — those are the calls that silently bypass authorization in most implementations, because they build queries by hand.

Health checks and circuit breaking. Laravel's HTTP layer should treat the proxy as a dependency: a Health check hitting the proxy's ping endpoint, plus a timeout + retry policy. A dead proxy should degrade search, not take down checkout.

Gotchas and Tips

Zero dependents, last release November 2020. No one is maintaining this. Pin the commit, fork it, and own the patches. Budget for ES 8 incompatibilities — the 2020-era code targets 6.x/7.x, and mapping types, _doc, and several APIs changed.

Rewriting _search is the easy 20%. The authorization surface also includes _msearch (multiple query bodies in one payload), _bulk, _search/template, q= query-string params, scroll and PIT contexts, and painless scripts. A proxy that filters only _search is bypassable in about four ways. Verify each endpoint is either rewritten or rejected — an allowlist of permitted APIs is safer than a denylist.

Reject, don't sanitize, unknown query shapes. If a request body contains a construct your rewriter doesn't understand, return 400. Silent pass-through is how tenant leaks ship.

Debugging the rewrite. Log the inbound and outbound bodies at the proxy (redact credentials), and diff them. When results look wrong, the question is almost always "did the filter get applied twice" — duplicate term filters are harmless but duplicate must_not clauses are not.

Network isolation is the actual security boundary. The proxy only means something if ES is unreachable from anywhere except the proxy's network segment — security groups, no public endpoint, no shared VPC peering to app subnets. Otherwise you've added latency and called it authorization.

Latency budget. You've added a hop and a Symfony bootstrap per request. Keep the proxy warm (opcache, php-fpm workers), enable persistent HTTP connections to ES, and measure p99 before and after. If search is on the critical path of page load, consider whether per-tenant filtered aliases get you there cheaper.

Extension points worth knowing. The bundle's authorization decision is the seam you'll customize — that's where your rules go. If you port to Laravel instead, the analogue is a Gate/Policy plus a query-builder decorator; keep the decision logic out of the controller.

Testing. Stand up an ES test container with two tenants' worth of fixtures, then assert that every endpoint returns zero cross-tenant hits — including _msearch and _bulk. Write these as the first tests; they're the spec for whatever you build on top.

Sanity check before committing. If your rule is genuinely "tenant A can only see tenant A's documents," Elasticsearch's built-in document-level security and API keys (7.x+) may cover it with far less code. This bundle was written when those weren't an option. Confirm the gap before you inherit an unmaintained proxy.

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.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager