Support comma-separated CORS_HEADER for multiple origins.

Parse CORS_HEADER as a list: * for all origins, or reflect matching
request Origin when multiple specific origins are configured. Add Vary:
Origin for the allowlist case. Update .env.example and CORS tests.
This commit is contained in:
2026-05-06 20:38:28 +00:00
parent 43d78862e3
commit 5dcd671043
4 changed files with 58 additions and 72 deletions

View File

@@ -63,10 +63,10 @@ type Config struct {
RateLimitPerMin int
ReservedUsernames []string
// CORS: exact origin list = FRONTEND_URL CORS_ORIGINS; loopback hosts if CORS_ALLOW_LOCALHOST.
CORSExtraOrigins []string
CORSAllowLocalhost bool
CORSAllowCredentials bool
// CORSOrigins is parsed from the CORS_HEADER env var (comma-separated).
// Use "*" to allow all origins, or list specific origins like
// "https://example.com,https://other.example".
CORSOrigins []string
}
func Load() (*Config, error) {
@@ -113,9 +113,7 @@ func Load() (*Config, error) {
LogLevel: env("LOG_LEVEL", "info"),
RateLimitPerMin: envInt("RATE_LIMIT_PER_MIN", 30),
ReservedUsernames: csv(env("RESERVED_USERNAMES", "")),
CORSExtraOrigins: csv(env("CORS_ORIGINS", "")),
CORSAllowLocalhost: envBool("CORS_ALLOW_LOCALHOST", true),
CORSAllowCredentials: envBool("CORS_ALLOW_CREDENTIALS", false),
CORSOrigins: csv(env("CORS_HEADER", "*")),
}
if err := Validate(c); err != nil {
@@ -181,22 +179,3 @@ func csvInt(v string) []int {
}
func (c *Config) Addr() string { return fmt.Sprintf(":%d", c.Port) }
// CORSExactOrigins lists allowed browser Origins for exact match (before loopback wildcard).
func (c *Config) CORSExactOrigins() []string {
seen := make(map[string]bool)
out := make([]string, 0, 4+len(c.CORSExtraOrigins))
add := func(s string) {
s = strings.TrimSpace(s)
if s == "" || seen[s] {
return
}
seen[s] = true
out = append(out, s)
}
add(c.FrontendURL)
for _, o := range c.CORSExtraOrigins {
add(o)
}
return out
}