{"record":{"id":"8c317e7d52bdcf66","repo":"seanmonstar/warp","slug":"cors-request-forbidden","errorCode":null,"errorMessage":"CORS request forbidden","messagePattern":"CORS request forbidden","errorType":"http","errorClass":"CorsForbidden","httpStatus":403,"severity":"error","filePath":"src/filters/cors.rs","lineNumber":513,"sourceCode":"            match validated {\n                Ok(Validated::Preflight(origin)) => {\n                    let preflight = Preflight {\n                        config: self.config.clone(),\n                        origin,\n                    };\n                    future::Either::Left(future::ok((Either::A((preflight,)),)))\n                }\n                Ok(Validated::Simple(origin)) => future::Either::Right(WrappedFuture {\n                    inner: self.inner.filter(Internal),\n                    wrapped: Some((self.config.clone(), origin)),\n                }),\n                Ok(Validated::NotCors) => future::Either::Right(WrappedFuture {\n                    inner: self.inner.filter(Internal),\n                    wrapped: None,\n                }),\n                Err(err) => {\n                    let rejection = crate::reject::known(CorsForbidden { kind: err });\n                    future::Either::Left(future::err(rejection.into()))\n                }\n            }\n        }\n    }\n\n    #[derive(Debug)]\n    pub struct Preflight {\n        config: Arc<Configured>,\n        origin: header::HeaderValue,\n    }\n\n    impl crate::reply::Reply for Preflight {\n        fn into_response(self) -> crate::reply::Response {\n            let mut res = crate::reply::Response::default();\n            self.config.append_preflight_headers(res.headers_mut());\n            res.headers_mut()\n                .insert(header::ACCESS_CONTROL_ALLOW_ORIGIN, self.origin);\n            res","sourceCodeStart":495,"sourceCodeEnd":531,"githubUrl":"https://github.com/seanmonstar/warp/blob/ff34d7213ed55ec342304aa7ff6ac4b351da9e66/src/filters/cors.rs#L495-L531","documentation":"This is warp's CorsForbidden rejection, produced by the CORS filter (src/filters/cors.rs) when an incoming request violates the configured CORS policy. The filter validates the Origin, request method, and request headers against the allow-list configured via warp::cors(); any mismatch yields err, which is wrapped into a known rejection with kind = CorsForbidden. The server deliberately rejects the request instead of serving it, because cross-origin access was not granted.","triggerScenarios":"A browser preflight (OPTIONS) or actual request carries an Origin header while the route is wrapped with cors(), and: the origin is not in allow_origin(), the method is not in allow_methods(), a request header is not in allow_headers(), credentials are used without allow_credentials(true), or a strict regex list is mis-typed.","commonSituations":"Frontend on localhost:3000 calling a backend on localhost:8080 without adding that origin to allow_origin; deploying behind a proxy that changes the Host/Origin; adding a new HTTP method or custom header in the client but forgetting to update allow_methods()/allow_headers(); forgetting to mount the cors filter on the OPTIONS preflight path.","solutions":["Add the browser origin (scheme + host + port, exactly) to cors().allow_origin([...])","Allow the failing method and headers via allow_methods() and allow_headers()","If cookies/auth headers are sent, call allow_credentials(true)","Ensure the cors filter is applied to the route handling the preflight request","For strict validation, verify the regex/allow list matches the origin string exactly (no trailing slash)"],"exampleFix":"// before\nlet cors = warp::cors().allow_methods(vec![\"GET\"]);\nlet routes = warp::get().and(warp::path(\"api\")).with(cors);\n// after\nlet cors = warp::cors()\n    .allow_origin(\"http://localhost:3000\")\n    .allow_methods(vec![\"GET\", \"POST\"])\n    .allow_headers(vec![\"content-type\", \"authorization\"])\n    .allow_credentials(true);\nlet routes = warp::get()\n    .and(warp::path(\"api\"))\n    .with(cors);","handlingStrategy":"fallback","validationCode":"// client-side preflight check before calling the API\nconst origin = window.location.origin;\nconst allowed = [\"http://localhost:3000\", \"https://app.example.com\"];\nif (!allowed.includes(origin)) console.warn(\"Origin will be rejected by CORS:\", origin);","typeGuard":"function isCorsRejection(rej: unknown): rej is { status: number } {\n  return typeof rej === \"object\" && rej !== null && \"status\" in rej && (rej as any).status === 403;\n}","tryCatchPattern":"try {\n  const res = await fetch(\"https://api.example.com/data\");\n} catch (e) {\n  // browsers mask CORS rejections as opaque network errors\n  console.error(\"Request blocked by CORS policy; check server allow_origin()\");\n}","preventionTips":["List every frontend origin (scheme+host+port) in allow_origin during setup","Enable allow_credentials(true) whenever cookies or Authorization headers cross origins","Test OPTIONS preflight with curl before wiring the frontend","Keep the cors filter applied to both actual and preflight routes","Never rely on wildcard origins in production"],"tags":["cors","http","warp","rejection"],"backgroundTag":"cors-request-forbidden","analyzedSha":"ff34d7213ed55ec342304aa7ff6ac4b351da9e66","analyzedAt":"2026-09-09T16:57:46.316Z","contentChangedAt":"2026-09-09T16:57:46.316Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}