astrid-runtime/astrid · error
CORS origin has a trailing slash; remove it (browsers send…
Error message
CORS origin {raw:?} has a trailing slash; remove it (browsers send `Origin:` without one) What it means
Validation guard in validate_cors_origin: the entry ends with a trailing slash, but browsers send Origin: without one and the response header is byte-matched, so 'https://app.example/' would silently never match; the entry is rejected with a pointed fix.
Solutions
- Delete the trailing slash from the origin entry
- Use the exact form the browser sends, e.g. https://app.example
- Re-test preflight after the fix
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at crates/astrid-gateway/src/config.rs:221 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/1f4565e0c43c6cf3.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-gateway/src/config.rs:221
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!(
"CORS origin {raw:?} must be ASCII-only (Punycode); browsers send the Punycoded form in `Origin:`. Use {parsed_ascii:?} instead."
);
}
Ok(())
}
#[cfg(test)]View on GitHub (pinned to affd8760f4)