Fail closed. If required config is missing or invalid, the app should refuse to start and say what is wrong, without printing secret values. Falling back to permissive defaults, such as no authentication or a development key, turns a deployment mistake into a security hole.
Why this is a security question
Twelve-factor stores config in environment variables and keeps it strictly separate from code, so config arrives from outside at start time. Config includes resource handles for backing services, credentials for external services, and per deploy values such as the hostname. Because config is supplied per deploy, it is also the thing most likely to be wrong in one deploy only: a missing variable in staging, a typo in production, a copy from the wrong environment.
How the app behaves in that case decides whether the mistake is noisy or silent. See what counts as config for where the line sits.
Insecure defaults to avoid
| Pattern | What can go wrong |
|---|---|
| Default to debug mode when a variable is absent | Verbose errors and tools exposed in production |
| Default signing key or secret in code | Anyone with the code can forge tokens |
| Authentication optional unless enabled | A missing flag means an open app |
| Permissive CORS when no origin list is set | Any site can call the API from a browser |
| Fall back to a local database or file path | Data written to the wrong place |
| Treat unparseable boolean as false for a security flag | A typo disables a control |
A fail closed startup
- List required settings in one place, with types.
- Validate at startup, before listening on a port. Check presence, format, ranges and allowed values.
- Refuse unsafe combinations, such as debug enabled with a production hostname, or TLS verification off outside development.
- Report names, not values. An error should say that the database URL is missing, not print it.
- Exit non zero so the orchestrator marks the rollout failed and keeps the old version serving. That ties into readiness: a pod that never becomes ready does not receive traffic.
- Test the failure. Add a test that starts the app with each required value removed.
Secrets need extra care
Secrets arrive through the same channel, so treat them with the same strictness. Check that a secret is present and has a plausible length, but do not log it. Where you offer a development convenience, make it impossible to enable in production, for example by refusing it when the environment name is production. Rotating a secret should not require a code change; dual credential rotation shows a pattern.
Config changes after start
Some apps reload config live, such as feature flags. Apply the same checks to a reload: reject invalid new config and keep the last good version. See what flags should default to when the service is unreachable for a related decision.
Try it yourself
Click through it at your own pace, or open it full screen.
Click through the code scan and look at how insecure defaults in code are reported and located.
Quick check
Should an app crash if one optional setting is missing?
No. Fail only for required settings, and give optional ones safe defaults that do not weaken security.
Is it okay to print the bad value in the error?
No, because it may be a secret. Print the setting name and what was wrong with it.
What about a default value for local development?
Keep it, but make it impossible to use when the environment is production, so the convenience cannot become a vulnerability.
Where Safeguard fits
Safeguard's policies and gates can review config changes in pull requests before they reach a release, and its compliance and evidence automation helps you keep records of those reviews. Try the free scan for the code side.
Sources
Last reviewed: 19 October 2026.