Search Across Multiple Drupal Sites with Pantheon SOLR

Abstract image showing a connected make believe world.

Search feels like a solved problem. Until you're managing a network of Drupal sites and your users expect to find content regardless of which one it lives on, that is. At that point, the question stops being "how do we add search?" and starts being "how do we build a unified search experience across an entire digital ecosystem?"

The obvious workaround is to bring in a third-party Solr provider. That works, but it means another vendor, another bill, and another service to monitor and secure. For organizations already invested in Pantheon's managed infrastructure, it further fragments the operational footprint rather than simplifying it.

At Aten, this challenge comes up regularly with clients running multi-site Drupal architectures on Pantheon. The solution we've landed on keeps everything within the platform, using four contributed modules working together as a hub-and-spoke proxy. This post walks through the architecture, why it exists, and how to implement it.

The Constraint You Can't Route Around

Pantheon's managed Solr is excellent: zero server administration, mTLS-secured connections, schema management handled through a purpose-built API. But it comes with a hard platform constraint: each Drupal site environment gets exactly one Solr core, and that core is network-isolated to that environment.

There is no platform mechanism to point a second Drupal site at another site's Solr instance. Each site can only talk to its own.

This is a reasonable security tradeoff for single-site use. But for a multi-site architecture where you need a single search index spanning many sites, it forces you to think architecturally rather than just reach for a configuration option.

The Architecture: One Hub, Many Clients

The solution is a hub-and-spoke proxy. One Drupal site (the hub) owns the Solr core and exposes it to other sites over authenticated HTTP. Every other site (the clients) routes its search queries through the hub instead of connecting to Solr directly.

From Pantheon's perspective, only the hub ever touches Solr. The constraint is satisfied. From Search API's perspective, every client is talking to a Solr instance in the normal way. The architecture sits between those two layers.

Four contributed modules make this work:

ModuleInstalled onRole
search_api_pantheonHubConnects to Pantheon Solr via env vars and mTLS
pantheon_solr_apiHubExposes Solr to authenticated clients via HTTP proxy
search_api_solr_proxyEach clientAbstract proxy base; no direct configuration
search_api_solr_proxy_pantheon_connectorEach clientRoutes queries through hub; manages API key via Key module

What Each Module Does

Hub: search_api_pantheon

This is the only module in the stack that speaks directly to Pantheon's Solr. It extends Search API Solr's standard connector and replaces the typical admin configuration form with auto-discovered values from Pantheon's environment variables (PANTHEON_INDEX_HOST, PANTHEON_INDEX_PORT, PANTHEON_INDEX_CORE, and others). On Pantheon environments, those fields are disabled in the UI; the platform owns them.

For transport, it swaps in a custom cURL adapter that uses the mTLS certificate Pantheon provisions at ~/certs/binding.pem. Schema uploads and core reloads go through Pantheon's proprietary endpoints rather than the standard Solr APIs.

Hub: pantheon_solr_api

This module is what actually opens the hub's Solr to the outside world. It exposes a set of Drupal routes at /solr-proxy/{action} that authenticated client sites can POST and GET against, and a standalone PHP file at /pantheon-solr-proxy.php for high-performance SELECT queries (more on that below).

Every request is validated against a shared API key before it reaches Solr. The key is read from a configured Key module entity (backed by a Pantheon Secret) and compared using hash_equals(), a timing-safe comparison that prevents key enumeration attacks. Only a hardcoded allowlist of Solr actions (select, update, update/json, admin/ping, admin/luke, config, and a handful of others) will be forwarded. Anything outside that list returns a 403.

Client registrations are tracked in Drupal config, keyed by the combination of each site's Search API site hash and index ID, the same namespace Search API Solr uses internally to scope documents.

Client: search_api_solr_proxy + search_api_solr_proxy_pantheon_connector

search_api_solr_proxy is a framework-only module. It provides the abstract SolrProxyConnectorBase class but is not useful on its own. search_api_solr_proxy_pantheon_connector is the Pantheon-specific implementation.

On each client site, the connector replaces the standard Search API server configuration (host, port, path, core) with a single hub_url field. A Guzzle middleware stack handles two things:

  1. Auth injection: every outbound request gets an X-Pantheon-Solr-Key header carrying the API key from the Key module entity.
  2. URL rewriting: SELECT queries are redirected to /pantheon-solr-proxy.php on the hub; everything else routes to /solr-proxy/{action} through Drupal.

Because client sites never manage the Solr schema directly, skip_schema_check is permanently enabled. The connector also auto-detects the Solr version by querying the hub's admin/system endpoint, falling back to 8.11.4 (Pantheon's current managed version) if the endpoint is unreachable.

How a Search Query Actually Travels

Here is the path of a typical user search on a client site:

  1. Search API builds a Solr SELECT query.
  2. The connector's Guzzle middleware intercepts it, adds the X-Pantheon-Solr-Key header, and rewrites the URL to https://hub.example.com/pantheon-solr-proxy.php?....
  3. The request arrives at the hub's web root. pantheon-solr-proxy.php runs as a standalone PHP script with no Drupal bootstrap. It validates the API key, constructs the Solr URL from Pantheon environment variables, and forwards the request using the mTLS certificate.
  4. Solr responds. The script returns the raw JSON response directly.
  5. Search API processes the results on the client site.

SELECT queries bypass Drupal's bootstrap entirely. On a warm server, the round trip through the proxy adds approximately 5ms of overhead. Writes, admin actions, and schema operations go through the Drupal controller instead, which adds around 150ms. That's acceptable for infrequent operations.

Getting It Set Up

On the hub site

composer require drupal/search_api_pantheon drupal/pantheon_solr_api
drush en search_api_pantheon pantheon_solr_api

Navigate to Administration > Configuration > Search and metadata > Pantheon Solr API. Select or create a Key entity pointing to the PANTHEON_SOLR_API_KEY Pantheon Secret. This is the shared key your client sites will use to authenticate.

Copy pantheon-solr-proxy.php (provided by pantheon_solr_api) to your hub site's web root. This is the fast-path script for SELECT queries.

On each client site

composer require drupal/search_api drupal/search_api_solr drupal/search_api_solr_proxy
drush en search_api search_api_solr search_api_solr_proxy_pantheon_connector

Create a Search API server using the Pantheon Solr Proxy connector. Set the Hub URL to your hub site's base URL. Configure the Key entity to use the same PANTHEON_SOLR_API_KEY secret.

Create your Search API index on that server as you normally would, with any entity types, fields, and processors you need.

Then run the registration command:

drush pantheon-solr-proxy:register

This command auto-discovers your Search API server and index, reads the site hash and index ID that Search API Solr uses to namespace your documents, and POSTs that registration to the hub. Back on the hub, run:

drush pantheon-solr-api:update-index

Repeat the client-side steps for each site in your network.

A Few Things Worth Knowing

Schema management stays on the hub. Only the hub ever uploads schema files to Pantheon. Client sites have skip_schema_check forced on. If your search requirements across sites are different enough to require separate schemas, this architecture assumes you can reconcile them into a single configset. In practice, the search_api_solr jump-start configset handles most requirements.

Document namespacing is automatic. Search API Solr already scopes every indexed document with a per-site hash and index ID prefix. Each client site's documents live in separate namespaces within the same Solr core. Cross-site queries need to either search all namespaces or be scoped deliberately; your Views or custom query code controls this.

The API key is a Pantheon Secret, not a config value. Keys stored in pantheon_solr_api.settings Drupal config hold a reference to a Key module entity, not the raw key. The actual secret is resolved at runtime from PANTHEON_SOLR_API_KEY. This keeps credentials out of your config exports and codebase.

Building Across Site Boundaries

Unified search across a multi-site Drupal architecture is one of those problems that looks straightforward until you try to implement it on a managed platform. Pantheon's security model solves a lot of problems, but it introduces constraints that require a deliberate architectural response.

The hub-and-spoke proxy described here is that response. It works within the platform's model, keeps credentials out of the codebase, and adds minimal latency to the critical read path.

If you're building a multi-site Drupal ecosystem and working through the hard architectural questions around search, shared data, and cross-site workflows, get in touch with the Aten team. This is the kind of problem we solve.

Drupal

Read This Next