astrid-runtime/astrid · error

CORS origin carries a query/fragment; origins are…

Error message

CORS origin {raw:?} carries a query/fragment; origins are scheme+host+port only

What it means

CORS origin lint in validate_cors_origin: the configured origin URL carries a non-empty path (and/or query/fragment). Browsers send `Origin:` as scheme+host+port only, so an origin with a path can never match a real preflight and would silently never allow the intended site; validation rejects it so operators fix the config instead of debugging failed CORS at runtime.

Solutions

  1. Remove the ?query and #fragment from the origin entry
  2. Keep only scheme://host[:port] in cors_allow_origins
  3. Fix the config source (template or script) that appended the extra parts
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at crates/astrid-gateway/src/config.rs:214 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/386a9755c7985c77. Report an issue: GitHub.

Appendix: source

Thrown at crates/astrid-gateway/src/config.rs:214

    if parsed.host_str().is_none() {
        anyhow::bail!("CORS origin {raw:?} has no host component");
    }
    // Browsers strip userinfo before sending `Origin:`, so a config
    // entry with embedded credentials can never match a real
    // preflight. Reject so operators don't silently misconfigure.
    if !parsed.username().is_empty() || parsed.password().is_some() {
        anyhow::bail!(
            "CORS origin {raw:?} carries userinfo (user:password); browsers strip it before sending `Origin:` so this can never match"
        );
    }
    if parsed.path() != "" && parsed.path() != "/" {
        anyhow::bail!(
            "CORS origin {raw:?} carries a path ({:?}); origins are scheme+host+port only",
            parsed.path()
        );
    }
    if parsed.query().is_some() || parsed.fragment().is_some() {
        anyhow::bail!(
            "CORS origin {raw:?} carries a query/fragment; origins are scheme+host+port only"
        );
    }
    // Disallow trailing-slash forms — browsers send `https://app.example`
    // (no slash) in `Origin:` and the response header is byte-matched.
    if raw.ends_with('/') {
        anyhow::bail!(
            "CORS origin {raw:?} has a trailing slash; remove it (browsers send `Origin:` without one)"
        );
    }
    // Reject a raw IDN — browsers transmit the Punycode (ASCII)
    // form in `Origin:`, so the bytes wouldn't match anyway. The
    // `Url` parser already normalizes the host to its ASCII form on
    // parse; if the *raw* string contained a non-ASCII character,
    // the parsed `origin()` ASCII-serialization won't equal `raw`.
    let parsed_ascii = parsed.origin().ascii_serialization();
    if parsed_ascii != raw {
        anyhow::bail!(

View on GitHub (pinned to affd8760f4)