Wann welche Konfigurationsart die richtige Wahl ist, und wo die Sicherheitsrisiken liegen
runtimeConfig eignet sich für Werte, die sich nach dem Build noch ändern können, etwa weil derselbe Docker-Container in mehreren Umgebungen mit unterschiedlichen API-Endpunkten läuft. Der Unterschied zwischen public- und privaten Werten entscheidet dabei direkt darüber, ob ein Wert versehentlich als Secret im Client landet.
Inhaltsverzeichnis
- 1. Build-Time und Runtime: Zwei unterschiedliche Zeitpunkte
- 2. Das Konzept von runtimeConfig
- 3. Public vs. private Runtime-Config-Werte
- 4. Praxisfall: Docker-Container mit unterschiedlichen Umgebungen
- 5. Namenskonvention für Environment-Variablen
- 6. Wann Build-Time-Variablen weiterhin nötig sind
- 7. Sicherheitsimplikationen bei exponierten Secrets
- 8. Best Practices im Umgang mit runtimeConfig
- 9. Fazit: Konfiguration bewusst nach Zeitpunkt trennen
- 10. Zusammenfassung
- 11. FAQ
1. Build-Time und Runtime: Zwei unterschiedliche Zeitpunkte
Bei einer klassischen Frontend-Anwendung werden Environment-Variablen üblicherweise während des Build-Prozesses in den JavaScript-Code eingebacken. Ein Wert wie process.env.API_URL wird dabei vom Build-Tool durch den tatsächlichen String ersetzt, und dieser String ist danach fest im ausgelieferten Bundle verankert, ganz unabhängig davon, in welcher Umgebung das Bundle später tatsächlich läuft.
Das funktioniert gut, solange für jede Umgebung, etwa Staging und Produktion, ein eigener Build erstellt wird. Sobald aber derselbe, bereits gebaute Docker-Container in mehreren Umgebungen mit unterschiedlichen Konfigurationswerten laufen soll, reicht dieser Ansatz nicht mehr aus, da der Wert bereits zur Build-Zeit unveränderlich in den Code eingebrannt wurde und sich zur Laufzeit nicht mehr ändern lässt.
2. Das Konzept von runtimeConfig
Nuxt löst dieses Problem mit runtimeConfig, einer Konfigurationsstruktur, deren Werte nicht zur Build-Zeit fest eingebrannt, sondern beim Start des Servers aus den tatsächlichen Environment-Variablen des laufenden Prozesses gelesen werden. Ein und derselbe gebaute Container kann dadurch in unterschiedlichen Umgebungen mit unterschiedlichen Werten gestartet werden, ohne dass ein erneuter Build nötig ist.
Definiert wird runtimeConfig zentral in der nuxt.config.ts, wobei die dort hinterlegten Werte gleichzeitig als Standardwerte und als Typdefinition dienen. Zur Laufzeit überschreibt Nuxt jeden Wert automatisch, sobald eine passend benannte Environment-Variable gesetzt ist, wodurch sich Konfiguration und Code sauber voneinander trennen lassen.
3. Public vs. private Runtime-Config-Werte
Innerhalb von runtimeConfig unterscheidet Nuxt strikt zwischen Werten auf der obersten Ebene, die ausschließlich auf dem Server verfügbar sind, und Werten innerhalb des verschachtelten public-Schlüssels, die sowohl auf dem Server als auch im Client zugänglich sind. Diese Trennung ist keine reine Konvention, sondern wird von Nuxt technisch durchgesetzt, indem nur der public-Teil tatsächlich in das an den Client ausgelieferte JavaScript-Bundle eingebettet wird.
Im folgenden Beispiel ist der Datenbank-Zugangsschlüssel ausschließlich serverseitig verfügbar, während die öffentliche API-Basis-URL bewusst über public auch dem Client zur Verfügung gestellt wird, etwa für clientseitige $fetch-Aufrufe direkt aus dem Browser heraus.
// nuxt.config.ts
export default defineNuxtConfig({
runtimeConfig: {
// nur serverseitig verfügbar
databaseApiKey: '',
public: {
// sowohl server- als auch clientseitig verfügbar
apiBase: 'https://api.example.com',
},
},
});
// server/api/orders.get.ts
export default defineEventHandler((event) => {
const config = useRuntimeConfig(event);
// databaseApiKey ist hier verfügbar, im Client jedoch NICHT
return $fetch(`${config.public.apiBase}/orders`, {
headers: { Authorization: `Bearer ${config.databaseApiKey}` },
});
});
4. Praxisfall: Docker-Container mit unterschiedlichen Umgebungen
In einem typischen Docker-basierten Deployment wird ein Image genau einmal gebaut und danach unverändert in mehreren Umgebungen wie Staging, Vorproduktion und Produktion ausgerollt. Würden API-Endpunkte oder Feature-Flags als Build-Time-Variablen behandelt, müsste für jede Umgebung ein eigenes Image gebaut werden, was den Grundsatz eines einmal gebauten, überall identischen Artefakts durchbricht.
Mit runtimeConfig bleibt das Image über alle Umgebungen hinweg identisch, lediglich die Environment-Variablen, die beim Start des Containers gesetzt werden, unterscheiden sich zwischen den Umgebungen. Das entspricht dem etablierten Prinzip, Konfiguration strikt von Code zu trennen, und reduziert gleichzeitig das Risiko, dass zwischen den Umgebungen unterschiedlicher Code getestet und ausgeliefert wird.
5. Namenskonvention für Environment-Variablen
Nuxt leitet den Namen der zugehörigen Environment-Variable automatisch aus dem verschachtelten Schlüsselpfad in runtimeConfig ab, wobei Großbuchstaben und Unterstriche verwendet werden. Ein Wert unter runtimeConfig.public.apiBase lässt sich beispielsweise über die Variable NUXT_PUBLIC_API_BASE setzen, während ein privater Wert wie runtimeConfig.databaseApiKey der Variable NUXT_DATABASE_API_KEY entspricht.
Diese automatische Ableitung reduziert Boilerplate erheblich, da kein manuelles Mapping zwischen Environment-Variablen und Konfigurationsstruktur gepflegt werden muss. In komplexeren Projekten mit vielen verschachtelten Konfigurationswerten empfiehlt es sich dennoch, die vollständige Liste der resultierenden Variablennamen zu dokumentieren, um Tippfehler beim Setzen der Variablen in der Deployment-Pipeline zu vermeiden.
6. Wann Build-Time-Variablen weiterhin nötig sind
Nicht jede Konfiguration eignet sich für runtimeConfig. Werte, die das Build-Ergebnis selbst beeinflussen, etwa welche Module aktiviert werden, welche CSS-Datei geladen wird oder ob bestimmter Code komplett aus dem Bundle entfernt werden soll, müssen zwangsläufig zur Build-Zeit bekannt sein, da sie das Ergebnis des Build-Prozesses selbst verändern und nicht erst nach dem Start des Servers relevant werden.
Für solche Fälle bleiben klassische Build-Time Environment-Variablen, die direkt innerhalb der nuxt.config.ts mit process.env ausgelesen werden, weiterhin der richtige Ansatz. Der entscheidende Unterschied liegt darin, ob ein Wert das Verhalten der bereits fertig gebauten Anwendung zur Laufzeit steuert, dann runtimeConfig, oder ob er den Build-Prozess selbst beeinflusst, dann eine klassische Build-Time-Variable.
7. Sicherheitsimplikationen bei exponierten Secrets
Der häufigste und gefährlichste Fehler im Umgang mit runtimeConfig besteht darin, einen sensiblen Wert wie einen API-Schlüssel, ein Datenbankpasswort oder ein privates Zertifikat versehentlich innerhalb des public-Schlüssels zu platzieren. Da alles innerhalb von public unverschlüsselt im an den Browser ausgelieferten JavaScript-Bundle landet, kann jeder Nutzer der Website diesen Wert einfach über die Browser-Entwicklertools einsehen.
Besonders tückisch ist, dass ein solcher Fehler während der lokalen Entwicklung meist unbemerkt bleibt, da alles scheinbar wie erwartet funktioniert, das Secret aber im Hintergrund bereits für jeden Website-Besucher sichtbar ist. Ein regelmäßiger Blick in das tatsächlich ausgelieferte Bundle, etwa über die Netzwerk-Ansicht der Browser-Entwicklertools, sowie eine bewusste Code-Review-Regel für alle Änderungen an runtimeConfig helfen, dieses Risiko zu minimieren.
8. Best Practices im Umgang mit runtimeConfig
Als Grundregel gilt: Jeder Wert startet als privater, serverseitiger Wert auf der obersten Ebene von runtimeConfig, und nur Werte, die nachweislich auch im Client benötigt werden, wandern bewusst und explizit in den public-Schlüssel. Diese defensive Grundhaltung verhindert, dass ein Wert aus Bequemlichkeit als public markiert wird, obwohl er es eigentlich gar nicht sein müsste.
Zusätzlich empfiehlt es sich, in der Deployment-Pipeline automatisiert zu prüfen, ob alle erwarteten Environment-Variablen beim Start des Containers tatsächlich gesetzt sind, um zu verhindern, dass die Anwendung mit leeren oder fehlenden Werten aus runtimeConfig in Produktion läuft. Ein fehlender, aber erwarteter Wert sollte den Deployment-Prozess idealerweise frühzeitig und klar sichtbar abbrechen lassen.
9. Fazit: Konfiguration bewusst nach Zeitpunkt trennen
Die Entscheidung zwischen runtimeConfig und klassischen Build-Time Environment-Variablen sollte sich stets danach richten, ob ein Wert das Verhalten des fertigen Builds zur Laufzeit steuert oder den Build-Prozess selbst beeinflusst. Für Docker-basierte Deployments mit einem einmal gebauten, in mehreren Umgebungen ausgerollten Image ist runtimeConfig in aller Regel die passendere und flexiblere Wahl.
Gleichzeitig verlangt runtimeConfig Disziplin bei der Trennung zwischen public- und privaten Werten, da ein einziger falsch platzierter Wert ausreicht, um ein Secret dauerhaft und für jeden Website-Besucher sichtbar zu exponieren. Wer diese Trennung konsequent einhält, gewinnt die Flexibilität von Runtime-Konfiguration, ohne die damit verbundenen Sicherheitsrisiken einzugehen.
| Aspekt | Build-Time Env-Variable | runtimeConfig |
|---|---|---|
| Zeitpunkt der Auflösung | Während des Build-Prozesses | Beim Start des Servers zur Laufzeit |
| Änderung ohne Rebuild | Nicht möglich | Möglich, über neu gesetzte Env-Variablen |
| Typischer Einsatz | Aktivierte Module, CSS-Auswahl | API-Endpunkte, Feature-Flags, Secrets |
| Docker-Eignung | Ein Image pro Umgebung nötig | Ein Image für alle Umgebungen |
| Sicherheitsrisiko | Wert steht im Quellcode/Build | Nur bei falscher public-Platzierung riskant |
Mironsoft
Vue-Architektur, Composition API und Nuxt-Performance
Vue-Anwendungen, die mit jedem Feature nicht komplizierter werden?
Wir prüfen bestehende Vue- und Nuxt-Projekte auf unstrukturierte Composables, ungenutzte Reaktivität und aufgeblähte Bundles und bauen daraus eine Architektur, die neue Features aufnimmt, ohne die Codebasis unübersichtlicher zu machen.
Architektur-Review
Composables, State-Management und Komponentenstruktur auf Wartbarkeit prüfen.
Performance-Audit
Reaktivitäts-Overhead, Bundle-Größe und Nuxt-Rendering-Strategie systematisch optimieren.
Nuxt-Integration
SSR/SSG-Setup und API-Anbindung robust und typsicher aufbauen.
10. Zusammenfassung
Runtime Config vs. Build-Time Env: Das Wichtigste auf einen Blick
runtimeConfig
Werte, die sich nach dem Build noch ändern können, aus Env gelesen
public-Schlüssel
Einzige Werte, die tatsächlich in den Client-Bundle gelangen
Docker-Vorteil
Ein Image für alle Umgebungen, Konfiguration über Env-Variablen
Größtes Risiko
Secret versehentlich im public-Schlüssel statt privat platziert