Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 11, 01:00 UTC
fastcgi: let configured env variables override request headers (#8358)
Kévin Dunglas committed
Nov 5, 2026 at 00:02 UTC
70dca34a36de12977673189b4789d45cfa81aaa5
2 files changed
+31
-5
modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go
+7
-5
@@ -437,11 +437,6 @@ func (t Transport) buildEnv(r *http.Request) (envVars, error) {
437
}
438
}
439
440
- // Add env variables from config (with support for placeholders in values)
441
- for key, value := range t.EnvVars {
442
- env[key] = repl.ReplaceAll(value, "")
443
- }
444
-
440
// Add all HTTP headers to env variables
441
for field, val := range r.Header {
442
switch http.CanonicalHeaderKey(field) {
@@ -460,6 +455,13 @@ func (t Transport) buildEnv(r *http.Request) (envVars, error) {
455
header = headerNameReplacer.Replace(header)
456
env["HTTP_"+header] = strings.Join(val, ", ")
457
}
458
+
459
+ // Add env variables from config (with support for placeholders in values)
460
+ // last, so a client header cannot override an HTTP_* variable set by the operator
461
+ for key, value := range t.EnvVars {
462
+ env[key] = repl.ReplaceAll(value, "")
463
+ }
464
+
465
return env, nil
466
}
467
modules/caddyhttp/reverseproxy/fastcgi/fastcgi_test.go
+24
@@ -509,6 +509,30 @@ func TestBuildEnvIgnoresHostHeaderField(t *testing.T) {
509
}
510
}
511
512
+// A client header must not override an HTTP_* variable set with the env subdirective.
513
+func TestBuildEnvConfiguredVarsOverrideHeaders(t *testing.T) {
514
+ t.Parallel()
515
+
516
+ r := httptest.NewRequest(http.MethodGet, "http://example.com/index.php", nil)
517
+ r.Header.Set("X-Forwarded-Proto", "http")
518
+ r.Header.Set("X-Auth-User", "admin")
519
+ repl := caddy.NewReplacer()
520
+ repl.Set("http.auth.user.id", "alice")
521
+ ctx := context.WithValue(r.Context(), caddy.ReplacerCtxKey, repl)
522
+ ctx = context.WithValue(ctx, caddyhttp.VarsCtxKey, map[string]any{})
523
+ ctx = context.WithValue(ctx, caddyhttp.OriginalRequestCtxKey, *r)
524
+
525
+ tr := Transport{EnvVars: map[string]string{
526
+ "HTTP_X_FORWARDED_PROTO": "https",
527
+ "HTTP_X_AUTH_USER": "{http.auth.user.id}",
528
+ }}
529
+ env, err := tr.buildEnv(r.WithContext(ctx))
530
+ require.NoError(t, err)
531
+
532
+ assert.Equal(t, "https", env["HTTP_X_FORWARDED_PROTO"])
533
+ assert.Equal(t, "alice", env["HTTP_X_AUTH_USER"])
534
+}
535
+
536
// A request path containing dot segments must not let the client select a
537
// different script than the one every path matcher in the chain evaluated.
538
// Matchers judge the cleaned path (see caddyhttp.MatchPath), so the transport