Why Drupal settings per environment need a deliberate plan
This section explains why a team moving a Drupal site from a developer laptop to staging and production needs a deliberate plan for environment-specific settings, secrets and trusted hosts. A single settings.php and a single exported configuration set cannot serve all three environments. Database credentials, file paths, caching backends, mail routing and the hostnames a site will answer to differ on each one, and a copy of the production file on a laptop or a staging server is a common route to leaked secrets and misrouted email. The work covers deciding what varies by environment, where each value lives and how the release process keeps them correct.
Two contributed modules handle much of the configuration side. According to the Configuration Split project page, Configuration Split keeps configuration that differs per environment, so a development site can enable modules or debugging settings that production must not carry. According to the Config Ignore project page, Config Ignore stops specified configuration from being overwritten on import, so values such as a site-specific setting can be edited in the user interface on each environment without the next deployment reverting them. Teams need these tools once more than one environment exports or imports configuration, which is the normal case for any site with a staging tier. Confirm the versions that match your Drupal release before you adopt either module.
Drupal itself is licensed under GPL-2.0-or-later, and there is no paid edition of Drupal, so the cost of this work is planning and build effort rather than a license fee. That effort is one-time for the initial design and recurs whenever a new environment, secret or module is added. When requesting a quote, ask the partner to list the environments in scope, the secret store and how it reaches the runtime, the configuration split and ignore rules, and the process for verifying trusted host patterns after each release. The sections that follow take these decisions in turn.
Which settings mechanism fits which environment need
This table compares the mechanisms teams use to vary Drupal settings between development, staging and production, and what each one costs to keep correct. Read each row by asking which kind of difference you need to manage (secrets, host names, file paths or exported configuration) and then check the maintenance column before committing to it.
| Mechanism | When it fits | Maintenance cost |
|---|---|---|
| Environment-specific settings.php includes | Stable per-environment values such as database host, file paths and reverse proxy settings that rarely change | Low to moderate. Every new setting needs a copy in each environment file, and drift is easy to miss in code review |
| settings.local.php outside the repository | Machine-specific or developer-specific overrides that must never be committed | Low. The file is created by hand or by provisioning, so confirm how it is rebuilt when a server is replaced |
| Environment variables or a secrets store feeding settings | Database passwords, API keys, hash salts and anything that must stay out of the codebase and in an audit trail | Moderate. Someone must own the secret inventory, rotation and the mapping from each variable to a settings key |
| trusted_host_patterns per environment | Every environment that serves requests needs its own host list, and unexpected Host headers must be rejected | Low per change, but each new domain, alias or preview URL needs an entry, so keep the list in the same review as DNS changes |
| Configuration Split | Settings or modules that must be enabled in staging but disabled in production, such as debugging or test-only modules | Moderate. Splits are defined in configuration and must be checked on every import, and the split rules need an owner |
| Config Ignore | Settings an editor changes in the UI, such as a site name or a third-party integration key, that must survive configuration import | Low to moderate. Ignored items no longer sync from code, so record what is ignored and why to avoid silent drift between environments |
How to set up settings per environment in order
This sequence moves Drupal's environment-specific settings out of the code path, so each environment gets its own hostnames, credentials and configuration without secrets or drift ending up in version control.
Set the no-secrets rule for settings.php
Agree with the development and operations leads that the committed settings.php contains no passwords, API keys, salts or database credentials. Review the current file and list every value that is secret or environment-specific, such as database connection details, hash salt, mail credentials and third-party tokens. Confirm the rule is written into the repository contributing guide and that the committed file only includes code that reads from the environment or from an include. Done when a search of the repository for known credential strings returns nothing and the rule is documented.
Create per-environment includes and a credentials path
Add an include pattern to settings.php that loads an environment-specific file, for example one for local, staging and production, plus a local settings file that is excluded from version control through the ignore rules. Place the credentials file outside the web root and outside the repository, with permissions limited to the web server user and the deployment account. Done when a test page or drush status on each environment reports the expected database and file paths, and git status shows the credentials file as untracked or ignored.
Define trusted_host_patterns for each hostname
Set $settings['trusted_host_patterns'] in each environment's include so that only the hostnames that environment serves are accepted. Use anchored regular expressions that escape the dots in each domain, and avoid wildcards that match unrelated hosts. Include internal hostnames only where health checks or load balancers need them. Done when a request with the correct hostname succeeds, and a request with an unlisted Host header returns the untrusted host error rather than rendering the site.
Set file paths and private directories per environment
Confirm that the public files directory, private files directory and temporary directory are defined per environment, and that the private directory sits outside the web root and is writable by the web server user only where needed. Point any object storage or shared file mounts at the environment's own bucket or volume so staging never writes into production storage. Done when uploading a test file on staging lands in the staging location and a file served from the private path returns access denied over HTTP.
Install Configuration Split and define a split per environment
Install the contributed Configuration Split module, following its project documentation, and create one split for each environment that needs different settings, such as development-only modules or staging-only mail handling. Assign each split to the environment that should activate it through its environment-specific settings, and keep shared configuration in the base sync directory. Done when a configuration export on the development environment shows the development-only modules in the split's folder and the production export does not include them.
Add Config Ignore for UI-managed items
Install Config Ignore and list the configuration items that editors or site builders manage through the interface on each environment, such as certain blocks, menus or mail settings that should not be overwritten on import. Keep the ignore list short and documented, because ignored items will drift from the repository. Done when an item changed in the staging user interface survives a configuration import, and the same item is still exported as expected in the repository on the environment that owns it.
Verify with an export and import on each environment
On each environment, run a configuration export and compare the result with the repository, then run a configuration import on a copy of the production database to confirm that the active split and ignore rules produce the expected result. Check that no credential, hostname or environment-only setting appears in the exported files, and that the trusted host check still passes after the import. Record the export diff and the import output for the release file. Done when the diff contains only intended changes and the import completes without errors on all environments.
Security risks in settings files and how to contain them
Settings.php and its included files decide where credentials live, which hostnames Drupal answers to and which directories are served to the public. Most of the risks below come from one habit: letting an environment-specific value travel with the code, the database or an export. Each card names the failure, why it matters on a Drupal site, and what to check before the next release.
Credentials committed to Git
Database passwords, hash salts and API keys written directly into settings.php end up in every clone, backup and CI log that touches the repository. Drupal uses the hash salt for session and one-time tokens, so a leaked salt weakens protections that are hard to see from the outside. Check that settings.php contains only includes or environment variable reads, that the repository history has been scanned for old secrets, and that any leaked value is rotated, not just removed in a new commit.
Production settings copied into staging
A straight copy of the production settings file moves production database credentials, file paths and mail or search endpoints into staging. Staging then writes to live data, sends real email to customers or runs imports against production services. Keep one settings file in the repository that loads a local override file or environment-specific include, and confirm during each release that the target environment's values are the ones in use.
Missing or broad trusted host patterns
Drupal rejects requests whose Host header does not match the trusted_host_patterns list, which blocks host header attacks used for password reset poisoning and cache abuse. An empty list or a pattern such as a wildcard that matches any domain removes that protection. Define the exact hostnames for each environment, including any internal health check names, and test that an unlisted host returns an error before launch.
Public and private file paths in the web root
If the private file path sits inside the web root, or the public file directory is served without the expected rules, uploaded documents can be downloaded by anyone who guesses a URL. The private files path should be outside the document root and set per environment, so that it resolves to a different location on each server. Confirm the path in the status report and request a known private file URL from an unauthenticated browser.
Secrets exported in configuration
Configuration exports store the values that site builders save in the database, such as mail server passwords, API tokens or third-party client secrets. Once exported, those values are committed, shared with developers and visible in pull requests. Keep secrets in environment variables or a secret store, use configuration overrides for the values that differ, and review each export diff before merging. Ask your secret store owner to confirm who can read and rotate these values in each environment.
Shared credentials across environments
When development, staging and production use the same database user, API key or SSO client secret, a compromise in a lower environment becomes a production incident. Separate credentials per environment let you revoke one without an outage elsewhere. Record which account each environment uses, restrict who can read production values, and confirm that rotation procedures for each credential are written down before go-live.
What differs between development, staging and production
Development is where developers change code, build custom modules and test configuration changes before they are shared. Its settings favor speed and visibility, so debug output and relaxed host checks are normal here. The main risk is that these relaxations leak into a shared environment, so the development settings should stay on developer machines or in an isolated development environment that no outside user can reach.
- Debug output: Verbose error display and development services, such as Twig debugging, help developers find faults quickly. These settings should never be enabled on a host that serves real visitors.
- Host patterns:
trusted_host_patternsmay allow local hostnames such aslocalhostor a developer's.testdomain. Keep these entries out of every shared environment. - Local credentials: Database and mail credentials can be simple and local, but they should not match any staging or production credential.
- Local file paths: Public and private file directories can sit inside the project folder, which keeps setup simple on a laptop.
- Disposable data: Development databases can be rebuilt from exports or fixtures at any time, so no one should treat them as a source of record.
Confirm that your development settings file is excluded from any production deployment and that developers cannot push host relaxations into the shared repository.
Staging should behave like production as closely as practical, so that release checks mean something. It is where teams test database updates, configuration import, performance and integrations against a copy of the production setup. Staging differs from production mainly in its data and credentials, and those differences need to be deliberate and documented.
- Production-like configuration: Caching, error display and file handling should match production wherever a difference could hide a release problem.
- Masked or sanitized data: Personal data copied from production should be masked or replaced before testers can reach the site. Confirm the masking process with your privacy and compliance owners.
- Separate credentials: Staging should use its own database, mail, API and secret values, so that a staging fault cannot touch production services.
- Host patterns:
trusted_host_patternsshould list only the staging hostnames, not the production domain or local names. - Restricted access: Staging is often easier to reach than production. Limit access with network controls or authentication so that copied data stays protected.
Confirm who can read the staging database and whether its backups are handled with the same care as production backups.
Production is the environment visitors and integrations reach, so its settings prioritize strict host validation, hidden internals and reliable performance. Each setting here should be explicit rather than inherited from a default or a developer file. Changes should go through the same review and release process as code, with a record of who approved them.
- Strict trusted hosts:
trusted_host_patternsshould list only the production domains, with escaped dots, so that requests with other Host headers are rejected. - Private file path: Set a private file directory outside the web root, and keep secrets and sensitive uploads there.
- Caching settings: Enable page and render caching and aggregation as your hosting design requires, and confirm the cache backend uses the production services.
- Error display: Errors should be logged to a monitored location without revealing stack traces or paths to visitors.
- Secrets from the environment: Database passwords, API keys and salts should come from the server or a secrets manager, not from files committed to the repository.
Confirm the production values by reading the effective configuration on the running site, and check that the deployment process cannot overwrite them.
Limits to confirm before you rely on this pattern
The settings-per-environment pattern depends on a few contributed modules, a secret source, the build pipeline and the hostnames a site actually answers to. Each card below names one limit to confirm in your own environment before you commit to the layout.
Module compatibility with your Drupal release
Configuration Split and Config Ignore are contributed modules, so their supported core versions are set by their own releases, not by Drupal core. Check the project pages against the Drupal release you run today and the one you plan to move to, including the Drupal 12 timeline. A split that works on your current release can break at the next major upgrade if the module has not caught up. Record the module versions you tested in your upgrade plan.
Secret source fit with the hosting platform
Database credentials, hash salts and API keys usually reach settings.php through environment variables, mounted files or a platform secret store. Confirm that your hosting platform can inject those values into the running PHP process at the right stage, and that the same mechanism works in staging and production without copying values into the repository. Ask who rotates each secret, how rotation reaches running workers and cron, and what happens to scheduled jobs during a rotation window.
Image rebuilds that need deploy-time changes
In a container or Kubernetes model, the image is built once and promoted across environments, so environment differences must arrive as runtime configuration. If your pipeline rebuilds the image for each environment, the artifact you tested in staging is not the one you ship to production. Confirm which settings, includes and split activations are baked into the image and which are supplied at deploy time, and whether your release process can prove that difference.
Per-site settings in multisite layouts
Core multisite runs many sites from one codebase, and each site directory needs its own settings.php, database credentials, file paths and trusted host patterns. A shared include can accidentally point one site at another site's database or files. Confirm how many sites share each environment file, how new sites are added without editing shared code, and how you verify that each site loads only its own settings after a deploy.
Trusted host behavior with real hostnames
trusted_host_patterns rejects requests whose Host header does not match a listed pattern, so a missed alias, a staging hostname or a load balancer health check path can produce errors that look like application faults. Test every public hostname, redirect, internal health probe and preview URL in each environment, including the ports and TLS names your proxy forwards. Keep the test results with the release record so a change to DNS or the ingress does not go unnoticed.
Configuration ignore rules and UI edits
Config Ignore keeps named configuration from being overwritten on import, which lets editors change some settings in the UI on production. The trade-off is drift: an ignored item no longer matches the exported configuration, so a later deploy will not restore it. Confirm which items are ignored, who may change them, and how you will review those values during audits and when you rebuild an environment from code.
Questions teams ask before changing settings.php
How much effort does per-environment settings.php work take?
The effort depends mostly on how many environments you run, how you host Drupal, and how far your existing configuration has drifted between them. A site with development, staging and production on one hosting model, with clean configuration exports, is a small, bounded job. Each additional environment adds hostnames, secret locations, database credentials and file paths to verify, and each hosting model adds its own way of injecting values. Drift is often the largest unknown: if staging and production were configured by hand at different times, someone has to find every difference before the split can be trusted. Ask a partner to inventory these three factors before quoting, and ask for the quote to list each environment and each hosting target separately.
Can our in-house team handle this without a partner?
Yes, if you have a developer who knows Drupal's settings files, the configuration system and your hosting platform, and who has time to test imports in each environment. The work itself is not exotic: it involves keeping secrets out of the repository, setting trusted_host_patterns for each hostname, and splitting configuration with contributed modules such as Configuration Split and Config Ignore. The risk is usually in the details, such as an overlooked hostname that breaks login or a configuration split that exports to the wrong place. A partner is most useful when you need an independent review of the result, or when the same team also owns the upgrade and release process that will test these settings again.
What do you need from us to start?
Expect to provide a complete list of hostnames for every environment, including preview and administrative URLs, because each one must appear in trusted_host_patterns. You will also need to name the secret store your organization uses, whether that is a vault, a cloud secrets service or an orchestrator's secret objects, and confirm who may read and rotate those secrets. Access to each environment is required for testing: a working copy of the codebase, read access to current configuration exports, and a way to deploy to staging. Document which settings are environment-specific today and which are assumed to be identical, since that list drives the configuration split design.
How do we confirm the settings are correct?
Confirmation rests on a short set of checks run in each environment. Requests to every approved hostname should load normally, while a request with an unlisted host header should be rejected. Secrets should be absent from the repository and present only in the runtime environment. Configuration import should produce no unexpected changes in production, and the files directory and private file path should resolve to the locations you intended. Run these checks after each deployment, not only at launch. Ask a partner to document each check with its expected result so your team can repeat it during audits and incident reviews.
What recurs after the settings are in place?
Some work repeats. Each new environment, preview site or hostname needs a settings entry and a host pattern check. Drupal upgrades can change settings, add new configuration or deprecate older keys, so the split and ignore rules need review at each upgrade. Security releases arrive on the third Wednesday of the month according to the Drupal core release schedule, and some of them require a rebuild and redeploy, which means the environment checks run again. Budget for these recurring checks as part of your normal release routine, and decide who owns host pattern changes so that a new hostname never reaches production without review.
What should we ask for in a written quote?
Ask for a scope that names every environment, hosting model and secret store in scope, and a list of the deliverables: the settings layout, the configuration split definitions, the host pattern list per environment, and the verification checklist. The quote should state what is one-time, such as the initial design and build, and what is recurring, such as retainer support or review at upgrade time. Ask which assumptions the price depends on, for example the number of environments or the condition of the existing configuration, and what happens if discovery finds more drift than expected. DeployOpen works from a written scope, so the scope document is where these terms should be agreed before work starts.
How trusted host patterns and config splits protect a Drupal site
Trusted host patterns decide which Host headers Drupal will answer. The trusted_host_patterns setting in settings.php lists the regular expressions for hostnames the site accepts. A request whose Host header matches none of them is rejected before Drupal builds a page, which blocks host header manipulation that can otherwise leak into generated links, password reset emails and cache keys. Each environment needs its own list because each serves different hostnames: a developer's local domain, a staging address that search engines should never index and the public production domains. A list copied from production to staging either lets unexpected hosts through or locks out the staging team. Confirm the exact hostnames for every environment, including internal load balancer names and health check hosts, and keep the patterns in environment-specific configuration rather than in a shared file.
Configuration Split keeps environment-specific configuration apart. The Configuration Split module defines named splits, such as one for development modules or one for staging-only settings, and activates them per environment so the same exported configuration can differ where it must. Without it, teams tend to edit configuration by hand on each server, and those edits drift out of version control. Config Ignore covers the opposite case: it stops specified configuration from being overwritten when configuration is imported, so values that the business manages in the admin interface survive a deployment. The Config Ignore project page describes this behavior. Decide early which settings belong in version control, which belong to a split and which are owned by the UI, because a mistake in that mapping shows up as a production change that nobody reviewed.
How DeployOpen works on this
DeployOpen plans and implements Drupal environment settings inside your own infrastructure, so development, staging and production each load the right database, file paths, trusted host patterns and configuration splits without secrets in the repository. Work follows a written scope agreed before any build starts.
How we scope
- Inventory every environment, its hostnames, database and file locations, and who may change each setting.
- Agree which secrets move out of the repository and which store (your vault or secrets manager) will hold them.
- Define the written scope, deliverables and timeline in writing before work starts, with engagements typically fixed-scope.
How we build
- Write settings.php includes per environment that read secrets from environment variables or mounted files, not committed values.
- Set trusted_host_patterns per environment and test that unlisted host headers are rejected before release.
- Configure Configuration Split and Config Ignore for environment-specific configuration, and confirm exports stay clean.
How we hand over
- Document each settings file, its load order, every variable it reads and the owner responsible for changing it.
- Provide a deployment checklist covering secret rotation, file path checks and config import steps per environment.
- Walk your platform and security teams through the setup and record decisions for later review.
How we support
- Review settings changes when Drupal core or contributed modules are updated, including new configuration that may need ignoring.
- Retest trusted host rules and secret handling after infrastructure or domain changes, as part of ongoing support.
- Ongoing support, upgrades and continuous testing are available as a retainer, scoped in writing separately from the initial build.

