[{"data":1,"prerenderedAt":2502},["ShallowReactive",2],{"nav":3,"page-\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fcors-middleware-configuration\u002F":580,"surround-\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fcors-middleware-configuration\u002F":2501},[4,186,386],{"title":5,"path":6,"stem":7,"children":8},"Advanced Pydantic Validation Serialization","\u002Fadvanced-pydantic-validation-serialization","advanced-pydantic-validation-serialization",[9,12,42,66,90,114,150,174],{"title":10,"path":6,"stem":11},"Advanced Pydantic Validation and Serialization","advanced-pydantic-validation-serialization\u002Findex",{"title":13,"path":14,"stem":15,"children":16},"Custom Validators and Field Constraints in Pydantic","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Findex",[17,18,24,30,36],{"title":13,"path":14,"stem":15},{"title":19,"path":20,"stem":21,"children":22},"Before, After and Wrap Validators in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fbefore-after-and-wrap-validators","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fbefore-after-and-wrap-validators\u002Findex",[23],{"title":19,"path":20,"stem":21},{"title":25,"path":26,"stem":27,"children":28},"Creating Reusable Custom Validators in Pydantic","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fcreating-reusable-custom-validators-in-pydantic","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fcreating-reusable-custom-validators-in-pydantic\u002Findex",[29],{"title":25,"path":26,"stem":27},{"title":31,"path":32,"stem":33,"children":34},"Cross-Field Validation Patterns in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fcross-field-validation-patterns","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fcross-field-validation-patterns\u002Findex",[35],{"title":31,"path":32,"stem":33},{"title":37,"path":38,"stem":39,"children":40},"Pydantic v2 Async Custom Validator: What to Do Instead","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fpydantic-v2-async-custom-validator","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fpydantic-v2-async-custom-validator\u002Findex",[41],{"title":37,"path":38,"stem":39},{"title":43,"path":44,"stem":45,"children":46},"JSON Schema Customization in Pydantic and FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fjson-schema-customization","advanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Findex",[47,48,54,60],{"title":43,"path":44,"stem":45},{"title":49,"path":50,"stem":51,"children":52},"Customizing OpenAPI Schema Generation in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fcustomizing-openapi-schema-generation-in-fastapi","advanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fcustomizing-openapi-schema-generation-in-fastapi\u002Findex",[53],{"title":49,"path":50,"stem":51},{"title":55,"path":56,"stem":57,"children":58},"Discriminated Unions in OpenAPI with Pydantic","\u002Fadvanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fdiscriminated-unions-in-openapi","advanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fdiscriminated-unions-in-openapi\u002Findex",[59],{"title":55,"path":56,"stem":57},{"title":61,"path":62,"stem":63,"children":64},"Examples in the OpenAPI Schema with FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fexamples-in-openapi-schema","advanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fexamples-in-openapi-schema\u002Findex",[65],{"title":61,"path":62,"stem":63},{"title":67,"path":68,"stem":69,"children":70},"Nested Model Serialization in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization","advanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Findex",[71,72,78,84],{"title":67,"path":68,"stem":69},{"title":73,"path":74,"stem":75,"children":76},"Excluding Fields Per Endpoint in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fexcluding-fields-per-endpoint","advanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fexcluding-fields-per-endpoint\u002Findex",[77],{"title":73,"path":74,"stem":75},{"title":79,"path":80,"stem":81,"children":82},"Handling Deeply Nested JSON Models Efficiently","\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fhandling-deeply-nested-json-models-efficiently","advanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fhandling-deeply-nested-json-models-efficiently\u002Findex",[83],{"title":79,"path":80,"stem":81},{"title":85,"path":86,"stem":87,"children":88},"Self-Referencing and Recursive Models in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fself-referencing-and-recursive-models","advanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fself-referencing-and-recursive-models\u002Findex",[89],{"title":85,"path":86,"stem":87},{"title":91,"path":92,"stem":93,"children":94},"Performance Optimization for Pydantic Models in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models","advanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Findex",[95,96,102,108],{"title":91,"path":92,"stem":93},{"title":97,"path":98,"stem":99,"children":100},"model_construct and When to Skip Validation in Pydantic","\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Fmodel-construct-when-to-skip-validation","advanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Fmodel-construct-when-to-skip-validation\u002Findex",[101],{"title":97,"path":98,"stem":99},{"title":103,"path":104,"stem":105,"children":106},"Pydantic Model Serialization Performance in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Fpydantic-model-serialization-performance","advanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Fpydantic-model-serialization-performance\u002Findex",[107],{"title":103,"path":104,"stem":105},{"title":109,"path":110,"stem":111,"children":112},"TypeAdapter for Non-Model Types in Pydantic","\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Ftypeadapter-for-non-model-types","advanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Ftypeadapter-for-non-model-types\u002Findex",[113],{"title":109,"path":110,"stem":111},{"title":115,"path":116,"stem":117,"children":118},"Pydantic V2 Migration Guide for FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Findex",[119,120,126,132,138,144],{"title":115,"path":116,"stem":117},{"title":121,"path":122,"stem":123,"children":124},"Migrate @validator to @field_validator in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmigrate-validator-to-field-validator","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmigrate-validator-to-field-validator\u002Findex",[125],{"title":121,"path":122,"stem":123},{"title":127,"path":128,"stem":129,"children":130},"Migrating from Pydantic v1 to v2 Without Breaking APIs","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmigrating-from-pydantic-v1-to-v2-without-breaking-apis","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmigrating-from-pydantic-v1-to-v2-without-breaking-apis\u002Findex",[131],{"title":127,"path":128,"stem":129},{"title":133,"path":134,"stem":135,"children":136},"model_config vs class Config in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmodel-config-vs-class-config","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmodel-config-vs-class-config\u002Findex",[137],{"title":133,"path":134,"stem":135},{"title":139,"path":140,"stem":141,"children":142},"Replacing json_encoders with field_serializer in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Freplacing-json-encoders-with-field-serializer","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Freplacing-json-encoders-with-field-serializer\u002Findex",[143],{"title":139,"path":140,"stem":141},{"title":145,"path":146,"stem":147,"children":148},"Migrating @root_validator to @model_validator in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Froot-validator-to-model-validator","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Froot-validator-to-model-validator\u002Findex",[149],{"title":145,"path":146,"stem":147},{"title":151,"path":152,"stem":153,"children":154},"Request Validation Patterns in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Frequest-validation-patterns","advanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Findex",[155,156,162,168],{"title":151,"path":152,"stem":153},{"title":157,"path":158,"stem":159,"children":160},"Optional vs Nullable Fields in Pydantic and FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Foptional-vs-nullable-fields","advanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Foptional-vs-nullable-fields\u002Findex",[161],{"title":157,"path":158,"stem":159},{"title":163,"path":164,"stem":165,"children":166},"Query, Path and Body Parameter Validation in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Fquery-path-and-body-parameter-validation","advanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Fquery-path-and-body-parameter-validation\u002Findex",[167],{"title":163,"path":164,"stem":165},{"title":169,"path":170,"stem":171,"children":172},"Validating File Uploads and Forms in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Fvalidating-file-uploads-and-forms","advanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Fvalidating-file-uploads-and-forms\u002Findex",[173],{"title":169,"path":170,"stem":171},{"title":175,"path":176,"stem":177,"children":178},"Type Hinting and IDE Integration in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration","advanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration\u002Findex",[179,180],{"title":175,"path":176,"stem":177},{"title":181,"path":182,"stem":183,"children":184},"Annotated Dependencies and Reusable Types in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration\u002Fannotated-dependencies-and-reusable-types","advanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration\u002Fannotated-dependencies-and-reusable-types\u002Findex",[185],{"title":181,"path":182,"stem":183},{"title":187,"path":188,"stem":189,"children":190},"Async Background Tasks Observability","\u002Fasync-background-tasks-observability","async-background-tasks-observability",[191,194,224,254,284,308,338,362],{"title":192,"path":188,"stem":193},"Async, Background Tasks, and Observability in FastAPI","async-background-tasks-observability\u002Findex",{"title":195,"path":196,"stem":197,"children":198},"Async Correctness and Concurrency in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Findex",[199,200,206,212,218],{"title":195,"path":196,"stem":197},{"title":201,"path":202,"stem":203,"children":204},"Concurrent Requests with asyncio.gather in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency\u002Fconcurrent-requests-with-asyncio-gather","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Fconcurrent-requests-with-asyncio-gather\u002Findex",[205],{"title":201,"path":202,"stem":203},{"title":207,"path":208,"stem":209,"children":210},"FastAPI async def vs def: Performance and When to Use Each","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency\u002Ffastapi-async-def-vs-def-performance","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Ffastapi-async-def-vs-def-performance\u002Findex",[211],{"title":207,"path":208,"stem":209},{"title":213,"path":214,"stem":215,"children":216},"Fixing Blocking Calls in Async FastAPI Routes","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency\u002Ffixing-blocking-calls-in-async-routes","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Ffixing-blocking-calls-in-async-routes\u002Findex",[217],{"title":213,"path":214,"stem":215},{"title":219,"path":220,"stem":221,"children":222},"Running Sync Code in a Threadpool in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency\u002Frunning-sync-code-in-a-threadpool","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Frunning-sync-code-in-a-threadpool\u002Findex",[223],{"title":219,"path":220,"stem":221},{"title":225,"path":226,"stem":227,"children":228},"Async Database Sessions in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions","async-background-tasks-observability\u002Fasync-database-sessions\u002Findex",[229,230,236,242,248],{"title":225,"path":226,"stem":227},{"title":231,"path":232,"stem":233,"children":234},"Async SQLAlchemy Session per Request in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Fasync-sqlalchemy-session-per-request","async-background-tasks-observability\u002Fasync-database-sessions\u002Fasync-sqlalchemy-session-per-request\u002Findex",[235],{"title":231,"path":232,"stem":233},{"title":237,"path":238,"stem":239,"children":240},"Fixing asyncpg Connection Pool Exhaustion in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Ffixing-asyncpg-pool-exhaustion","async-background-tasks-observability\u002Fasync-database-sessions\u002Ffixing-asyncpg-pool-exhaustion\u002Findex",[241],{"title":237,"path":238,"stem":239},{"title":243,"path":244,"stem":245,"children":246},"Testing with Async Database Fixtures in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Ftesting-with-async-database-fixtures","async-background-tasks-observability\u002Fasync-database-sessions\u002Ftesting-with-async-database-fixtures\u002Findex",[247],{"title":243,"path":244,"stem":245},{"title":249,"path":250,"stem":251,"children":252},"Transaction Management and Rollback in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Ftransaction-management-and-rollback","async-background-tasks-observability\u002Fasync-database-sessions\u002Ftransaction-management-and-rollback\u002Findex",[253],{"title":249,"path":250,"stem":251},{"title":255,"path":256,"stem":257,"children":258},"Background Task Processing in FastAPI","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing","async-background-tasks-observability\u002Fbackground-task-processing\u002Findex",[259,260,266,272,278],{"title":255,"path":256,"stem":257},{"title":261,"path":262,"stem":263,"children":264},"FastAPI BackgroundTasks vs Celery vs ARQ","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Ffastapi-backgroundtasks-vs-celery-vs-arq","async-background-tasks-observability\u002Fbackground-task-processing\u002Ffastapi-backgroundtasks-vs-celery-vs-arq\u002Findex",[265],{"title":261,"path":262,"stem":263},{"title":267,"path":268,"stem":269,"children":270},"Retry and Idempotency for FastAPI Background Tasks","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Fretry-and-idempotency-for-tasks","async-background-tasks-observability\u002Fbackground-task-processing\u002Fretry-and-idempotency-for-tasks\u002Findex",[271],{"title":267,"path":268,"stem":269},{"title":273,"path":274,"stem":275,"children":276},"Running ARQ Workers with FastAPI","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Frunning-arq-workers-with-fastapi","async-background-tasks-observability\u002Fbackground-task-processing\u002Frunning-arq-workers-with-fastapi\u002Findex",[277],{"title":273,"path":274,"stem":275},{"title":279,"path":280,"stem":281,"children":282},"When FastAPI BackgroundTasks Silently Fails","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Fwhen-backgroundtasks-silently-fails","async-background-tasks-observability\u002Fbackground-task-processing\u002Fwhen-backgroundtasks-silently-fails\u002Findex",[283],{"title":279,"path":280,"stem":281},{"title":285,"path":286,"stem":287,"children":288},"Caching Strategies in FastAPI","\u002Fasync-background-tasks-observability\u002Fcaching-strategies","async-background-tasks-observability\u002Fcaching-strategies\u002Findex",[289,290,296,302],{"title":285,"path":286,"stem":287},{"title":291,"path":292,"stem":293,"children":294},"Cache Invalidation Patterns in FastAPI","\u002Fasync-background-tasks-observability\u002Fcaching-strategies\u002Fcache-invalidation-patterns-in-fastapi","async-background-tasks-observability\u002Fcaching-strategies\u002Fcache-invalidation-patterns-in-fastapi\u002Findex",[295],{"title":291,"path":292,"stem":293},{"title":297,"path":298,"stem":299,"children":300},"Caching Dependency Results in FastAPI","\u002Fasync-background-tasks-observability\u002Fcaching-strategies\u002Fcaching-dependency-results","async-background-tasks-observability\u002Fcaching-strategies\u002Fcaching-dependency-results\u002Findex",[301],{"title":297,"path":298,"stem":299},{"title":303,"path":304,"stem":305,"children":306},"Redis Response Caching in FastAPI","\u002Fasync-background-tasks-observability\u002Fcaching-strategies\u002Fredis-response-caching-in-fastapi","async-background-tasks-observability\u002Fcaching-strategies\u002Fredis-response-caching-in-fastapi\u002Findex",[307],{"title":303,"path":304,"stem":305},{"title":309,"path":310,"stem":311,"children":312},"Observability and Tracing in FastAPI","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing","async-background-tasks-observability\u002Fobservability-and-tracing\u002Findex",[313,314,320,326,332],{"title":309,"path":310,"stem":311},{"title":315,"path":316,"stem":317,"children":318},"Correlating Logs, Traces and Errors in FastAPI","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002Fcorrelating-logs-traces-and-errors","async-background-tasks-observability\u002Fobservability-and-tracing\u002Fcorrelating-logs-traces-and-errors\u002Findex",[319],{"title":315,"path":316,"stem":317},{"title":321,"path":322,"stem":323,"children":324},"Instrumenting FastAPI with OpenTelemetry","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002Finstrumenting-fastapi-with-opentelemetry","async-background-tasks-observability\u002Fobservability-and-tracing\u002Finstrumenting-fastapi-with-opentelemetry\u002Findex",[325],{"title":321,"path":322,"stem":323},{"title":327,"path":328,"stem":329,"children":330},"Prometheus Metrics for FastAPI","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002Fprometheus-metrics-for-fastapi","async-background-tasks-observability\u002Fobservability-and-tracing\u002Fprometheus-metrics-for-fastapi\u002Findex",[331],{"title":327,"path":328,"stem":329},{"title":333,"path":334,"stem":335,"children":336},"Structured JSON Logging with Request IDs in FastAPI","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002Fstructured-json-logging-with-request-ids","async-background-tasks-observability\u002Fobservability-and-tracing\u002Fstructured-json-logging-with-request-ids\u002Findex",[337],{"title":333,"path":334,"stem":335},{"title":339,"path":340,"stem":341,"children":342},"Rate Limiting and Throttling in FastAPI","\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling","async-background-tasks-observability\u002Frate-limiting-throttling\u002Findex",[343,344,350,356],{"title":339,"path":340,"stem":341},{"title":345,"path":346,"stem":347,"children":348},"FastAPI Rate Limiting with Redis and SlowAPI","\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling\u002Ffastapi-rate-limiting-with-redis-slowapi","async-background-tasks-observability\u002Frate-limiting-throttling\u002Ffastapi-rate-limiting-with-redis-slowapi\u002Findex",[349],{"title":345,"path":346,"stem":347},{"title":351,"path":352,"stem":353,"children":354},"Per-User Token Bucket Throttling in FastAPI","\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling\u002Fper-user-token-bucket-throttling","async-background-tasks-observability\u002Frate-limiting-throttling\u002Fper-user-token-bucket-throttling\u002Findex",[355],{"title":351,"path":352,"stem":353},{"title":357,"path":358,"stem":359,"children":360},"Rate Limit Headers and 429 Responses in FastAPI","\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling\u002Frate-limit-headers-and-429-responses","async-background-tasks-observability\u002Frate-limiting-throttling\u002Frate-limit-headers-and-429-responses\u002Findex",[361],{"title":357,"path":358,"stem":359},{"title":363,"path":364,"stem":365,"children":366},"Testing FastAPI Applications","\u002Fasync-background-tasks-observability\u002Ftesting-fastapi-applications","async-background-tasks-observability\u002Ftesting-fastapi-applications\u002Findex",[367,368,374,380],{"title":363,"path":364,"stem":365},{"title":369,"path":370,"stem":371,"children":372},"Mocking External Services in FastAPI Tests","\u002Fasync-background-tasks-observability\u002Ftesting-fastapi-applications\u002Fmocking-external-services-in-tests","async-background-tasks-observability\u002Ftesting-fastapi-applications\u002Fmocking-external-services-in-tests\u002Findex",[373],{"title":369,"path":370,"stem":371},{"title":375,"path":376,"stem":377,"children":378},"TestClient vs httpx AsyncClient in FastAPI","\u002Fasync-background-tasks-observability\u002Ftesting-fastapi-applications\u002Ftestclient-vs-httpx-asyncclient","async-background-tasks-observability\u002Ftesting-fastapi-applications\u002Ftestclient-vs-httpx-asyncclient\u002Findex",[379],{"title":375,"path":376,"stem":377},{"title":381,"path":382,"stem":383,"children":384},"Testing Async FastAPI Endpoints with pytest-asyncio","\u002Fasync-background-tasks-observability\u002Ftesting-fastapi-applications\u002Ftesting-async-endpoints-with-pytest-asyncio","async-background-tasks-observability\u002Ftesting-fastapi-applications\u002Ftesting-async-endpoints-with-pytest-asyncio\u002Findex",[385],{"title":381,"path":382,"stem":383},{"title":387,"path":388,"stem":389,"children":390},"Core Architecture Routing Patterns","\u002Fcore-architecture-routing-patterns","core-architecture-routing-patterns",[391,394,412,436,472,496,526,556],{"title":392,"path":388,"stem":393},"FastAPI Core Architecture and Routing Patterns","core-architecture-routing-patterns\u002Findex",{"title":395,"path":396,"stem":397,"children":398},"Application Factory Patterns in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns","core-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Findex",[399,400,406],{"title":395,"path":396,"stem":397},{"title":401,"path":402,"stem":403,"children":404},"FastAPI App Factory Pattern for Testing and Deployment","\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Ffastapi-app-factory-pattern-for-testing-and-deployment","core-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Ffastapi-app-factory-pattern-for-testing-and-deployment\u002Findex",[405],{"title":401,"path":402,"stem":403},{"title":407,"path":408,"stem":409,"children":410},"Lifespan Events vs Startup and Shutdown in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Flifespan-events-vs-startup-shutdown","core-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Flifespan-events-vs-startup-shutdown\u002Findex",[411],{"title":407,"path":408,"stem":409},{"title":413,"path":414,"stem":415,"children":416},"Configuration Management in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management","core-architecture-routing-patterns\u002Fconfiguration-management\u002Findex",[417,418,424,430],{"title":413,"path":414,"stem":415},{"title":419,"path":420,"stem":421,"children":422},"Managing Environment Variables with Pydantic Settings","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management\u002Fmanaging-environment-variables-with-pydantic-settings","core-architecture-routing-patterns\u002Fconfiguration-management\u002Fmanaging-environment-variables-with-pydantic-settings\u002Findex",[423],{"title":419,"path":420,"stem":421},{"title":425,"path":426,"stem":427,"children":428},"Pydantic Settings vs Dynaconf vs python-decouple","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management\u002Fpydantic-settings-vs-dynaconf-vs-python-decouple","core-architecture-routing-patterns\u002Fconfiguration-management\u002Fpydantic-settings-vs-dynaconf-vs-python-decouple\u002Findex",[429],{"title":425,"path":426,"stem":427},{"title":431,"path":432,"stem":433,"children":434},"Secrets and .env Files Per Environment in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management\u002Fsecrets-and-env-files-per-environment","core-architecture-routing-patterns\u002Fconfiguration-management\u002Fsecrets-and-env-files-per-environment\u002Findex",[435],{"title":431,"path":432,"stem":433},{"title":437,"path":438,"stem":439,"children":440},"Dependency Injection Strategies in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Findex",[441,442,448,454,460,466],{"title":437,"path":438,"stem":439},{"title":443,"path":444,"stem":445,"children":446},"Best Practices for FastAPI Dependency Injection","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fbest-practices-for-fastapi-dependency-injection","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fbest-practices-for-fastapi-dependency-injection\u002Findex",[447],{"title":443,"path":444,"stem":445},{"title":449,"path":450,"stem":451,"children":452},"Dependency Caching and use_cache in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fdependency-caching-and-use-cache","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fdependency-caching-and-use-cache\u002Findex",[453],{"title":449,"path":450,"stem":451},{"title":455,"path":456,"stem":457,"children":458},"Fixing FastAPI Dependency Injection Circular Imports","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Ffastapi-dependency-injection-circular-import-fix","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Ffastapi-dependency-injection-circular-import-fix\u002Findex",[459],{"title":455,"path":456,"stem":457},{"title":461,"path":462,"stem":463,"children":464},"Overriding Dependencies in FastAPI Tests","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Foverriding-dependencies-in-tests","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Foverriding-dependencies-in-tests\u002Findex",[465],{"title":461,"path":462,"stem":463},{"title":467,"path":468,"stem":469,"children":470},"Yield Dependencies and Cleanup Order in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fyield-dependencies-and-cleanup-order","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fyield-dependencies-and-cleanup-order\u002Findex",[471],{"title":467,"path":468,"stem":469},{"title":473,"path":474,"stem":475,"children":476},"Error Handling and Global Exceptions in FastAPI","\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions","core-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Findex",[477,478,484,490],{"title":473,"path":474,"stem":475},{"title":479,"path":480,"stem":481,"children":482},"Customising Validation Error Responses in FastAPI","\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fcustomising-validation-error-responses","core-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fcustomising-validation-error-responses\u002Findex",[483],{"title":479,"path":480,"stem":481},{"title":485,"path":486,"stem":487,"children":488},"Global Exception Handlers for Consistent API Responses","\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fglobal-exception-handlers-for-consistent-api-responses","core-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fglobal-exception-handlers-for-consistent-api-responses\u002Findex",[489],{"title":485,"path":486,"stem":487},{"title":491,"path":492,"stem":493,"children":494},"HTTPException vs Custom Exception Classes in FastAPI","\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fhttpexception-vs-custom-exception-classes","core-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fhttpexception-vs-custom-exception-classes\u002Findex",[495],{"title":491,"path":492,"stem":493},{"title":497,"path":498,"stem":499,"children":500},"Middleware Implementation in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Findex",[501,502,508,514,520],{"title":497,"path":498,"stem":499},{"title":503,"path":504,"stem":505,"children":506},"CORS Middleware Configuration in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fcors-middleware-configuration","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fcors-middleware-configuration\u002Findex",[507],{"title":503,"path":504,"stem":505},{"title":509,"path":510,"stem":511,"children":512},"Implementing Custom Middleware for Request Tracing","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fimplementing-custom-middleware-for-request-tracing","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fimplementing-custom-middleware-for-request-tracing\u002Findex",[513],{"title":509,"path":510,"stem":511},{"title":515,"path":516,"stem":517,"children":518},"Middleware Execution Order in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fmiddleware-execution-order","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fmiddleware-execution-order\u002Findex",[519],{"title":515,"path":516,"stem":517},{"title":521,"path":522,"stem":523,"children":524},"Middleware vs Dependencies: When to Use Which","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fmiddleware-vs-dependencies-when-to-use-which","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fmiddleware-vs-dependencies-when-to-use-which\u002Findex",[525],{"title":521,"path":522,"stem":523},{"title":527,"path":528,"stem":529,"children":530},"Modular Router Organization in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Findex",[531,532,538,544,550],{"title":527,"path":528,"stem":529},{"title":533,"path":534,"stem":535,"children":536},"APIRouter Prefix vs Sub-Application Mounting in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization\u002Fapirouter-prefix-vs-sub-application-mounting","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Fapirouter-prefix-vs-sub-application-mounting\u002Findex",[537],{"title":533,"path":534,"stem":535},{"title":539,"path":540,"stem":541,"children":542},"How to Structure Large FastAPI Projects for Scale","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization\u002Fhow-to-structure-large-fastapi-projects-for-scale","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Fhow-to-structure-large-fastapi-projects-for-scale\u002Findex",[543],{"title":539,"path":540,"stem":541},{"title":545,"path":546,"stem":547,"children":548},"Router Tags and OpenAPI Grouping in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization\u002Frouter-tags-and-openapi-grouping","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Frouter-tags-and-openapi-grouping\u002Findex",[549],{"title":545,"path":546,"stem":547},{"title":551,"path":552,"stem":553,"children":554},"Versioning APIs with FastAPI Routers","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization\u002Fversioning-apis-with-routers","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Fversioning-apis-with-routers\u002Findex",[555],{"title":551,"path":552,"stem":553},{"title":557,"path":558,"stem":559,"children":560},"The FastAPI Request\u002FResponse Lifecycle","\u002Fcore-architecture-routing-patterns\u002Frequest-response-lifecycle","core-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Findex",[561,562,568,574],{"title":557,"path":558,"stem":559},{"title":563,"path":564,"stem":565,"children":566},"How a Request Flows Through FastAPI","\u002Fcore-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fhow-a-request-flows-through-fastapi","core-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fhow-a-request-flows-through-fastapi\u002Findex",[567],{"title":563,"path":564,"stem":565},{"title":569,"path":570,"stem":571,"children":572},"Response Model and Serialization Order","\u002Fcore-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fresponse-model-and-serialization-order","core-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fresponse-model-and-serialization-order\u002Findex",[573],{"title":569,"path":570,"stem":571},{"title":575,"path":576,"stem":577,"children":578},"Streaming and File Responses in FastAPI","\u002Fcore-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fstreaming-and-file-responses","core-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fstreaming-and-file-responses\u002Findex",[579],{"title":575,"path":576,"stem":577},{"id":581,"title":503,"body":582,"dateModified":2469,"datePublished":2469,"description":2470,"extension":2471,"faq":2472,"howto":2484,"meta":2485,"navigation":932,"path":504,"seo":2498,"stem":505,"type":2499,"__hash__":2500},"content\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fcors-middleware-configuration\u002Findex.md",{"type":583,"value":584,"toc":2453},"minimark",[585,589,596,652,661,666,682,685,814,818,828,860,872,883,887,1064,1094,1098,1104,1787,1792,1799,1827,1831,1837,1855,1859,1865,1904,1908,1914,1920,1930,1934,1940,1957,1992,1996,1999,2051,2069,2072,2239,2243,2257,2278,2295,2307,2317,2321,2333,2342,2359,2378,2387,2405,2409,2449],[586,587,503],"h1",{"id":588},"cors-middleware-configuration-in-fastapi",[590,591,592],"p",{},[593,594,595],"strong",{},"Key takeaways:",[597,598,599,612,625,635,646],"ul",{},[600,601,602,603,606,607,611],"li",{},"CORS is enforced by the ",[593,604,605],{},"browser",". A blocked request almost always got a ",[608,609,610],"code",{},"200"," from your server.",[600,613,614,617,618,621,622,624],{},[608,615,616],{},"CORSMiddleware"," answers ",[608,619,620],{},"OPTIONS"," preflights itself, before routing — you never write an ",[608,623,620],{}," route.",[600,626,627,630,631,634],{},[608,628,629],{},"allow_credentials=True"," with ",[608,632,633],{},"allow_origins=[\"*\"]"," is invalid per spec. Starlette does not error; it echoes the caller's origin, effectively trusting everyone.",[600,636,637,638,641,642,645],{},"A disallowed origin gets a response with ",[593,639,640],{},"no"," ",[608,643,644],{},"access-control-allow-origin"," header. That absence is the error.",[600,647,648,649,651],{},"Add ",[608,650,616],{}," last so it ends up outermost and its headers survive errors from inner layers.",[590,653,654,655,660],{},"This page is part of ",[656,657,659],"a",{"href":658},"\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002F","middleware implementation",", and unlike most middleware topics it is mostly about a component you should not write yourself — only configure correctly.",[662,663,665],"h2",{"id":664},"the-problem-this-solves","The Problem This Solves",[590,667,668,669,672,673,675,676,678,679,681],{},"The browser console says ",[608,670,671],{},"No 'Access-Control-Allow-Origin' header is present on the requested resource",". You add ",[608,674,616],{},", it still fails. You add ",[608,677,633],{},", the error changes to something about credentials. You add ",[608,680,629],{},", and now it fails differently.",[590,683,684],{},"Every step of that loop is guesswork, because the one thing you cannot see from the browser is what the server actually sent. This page shows the real headers for each configuration so you can compare them against yours.",[686,687,688,810],"figure",{},[689,690,698,699,698,703,698,707,698,716,698,723,698,728,698,733,698,738,698,743,698,748,698,754,698,759,698,765,698,770,698,773,698,778,698,782,698,786,698,789,698,794,698,798,698,801,698,805],"svg",{"viewBox":691,"role":692,"ariaLabelledBy":693,"xmlns":696,"style":697},"0 0 720 320","img",[694,695],"cors-t","cors-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;height:auto;max-width:720px;margin:2rem 0","\n  ",[700,701,702],"title",{"id":694},"Preflight and actual request flow through CORSMiddleware",[704,705,706],"desc",{"id":695},"The browser sends an OPTIONS preflight that CORSMiddleware answers before routing. If the response allows the method and headers, the browser sends the real request, which reaches the route and returns with an allow-origin header.",[708,709],"rect",{"x":710,"y":711,"width":712,"height":713,"rx":714,"style":715},"30","24","150","52","8","fill:#E0F2F1;stroke:#00796B;stroke-width:1.8px",[717,718,722],"text",{"x":719,"y":720,"style":721},"105","55","text-anchor:middle;fill:#00695C;font:700 13px sans-serif","Browser",[708,724],{"x":725,"y":711,"width":726,"height":713,"rx":714,"style":727},"290","180","fill:#FFFFFF;stroke:#00796B;stroke-width:1.8px",[717,729,616],{"x":730,"y":731,"style":732},"380","47","text-anchor:middle;fill:currentColor;font:700 12px sans-serif",[717,734,737],{"x":730,"y":735,"style":736},"64","text-anchor:middle;fill:#4B5563;font:400 11px sans-serif","outermost layer",[708,739],{"x":740,"y":711,"width":741,"height":713,"rx":714,"style":742},"560","130","fill:#FFFFFF;stroke:currentColor;stroke-width:1.5px",[717,744,747],{"x":745,"y":720,"style":746},"625","text-anchor:middle;fill:currentColor;font:700 13px sans-serif","Route",[749,750],"line",{"x1":726,"y1":751,"x2":752,"y2":751,"style":753},"120","288","stroke:#00796B;stroke-width:1.6px",[755,756],"polygon",{"points":757,"style":758},"288,115 298,120 288,125","fill:#00796B",[717,760,764],{"x":761,"y":762,"style":763},"234","112","text-anchor:middle;fill:currentColor;font:600 11px sans-serif","1. OPTIONS",[749,766],{"x1":767,"y1":768,"x2":769,"y2":768,"style":753},"298","156","190",[755,771],{"points":772,"style":758},"190,151 180,156 190,161",[717,774,777],{"x":775,"y":776,"style":763},"240","148","2. allow headers",[717,779,781],{"x":775,"y":780,"style":736},"176","answered without routing",[749,783],{"x1":726,"y1":784,"x2":785,"y2":784,"style":753},"216","558",[755,787],{"points":788,"style":758},"558,211 568,216 558,221",[717,790,793],{"x":791,"y":792,"style":763},"370","208","3. real GET or POST",[749,795],{"x1":796,"y1":797,"x2":769,"y2":797,"style":753},"568","252",[755,799],{"points":800,"style":758},"190,247 180,252 190,257",[717,802,804],{"x":791,"y":803,"style":763},"244","4. response plus allow-origin",[717,806,809],{"x":791,"y":807,"style":808},"292","text-anchor:middle;fill:#4B5563;font:400 12px sans-serif","Missing allow-origin at step 4 is what the console reports.",[811,812,813],"figcaption",{},"The preflight never reaches your routes. CORSMiddleware answers it and only then does the real request proceed.",[662,815,817],{"id":816},"why-it-happens-the-browser-is-the-enforcer","Why It Happens: the Browser Is the Enforcer",[590,819,820,821,824,825,827],{},"Cross-origin restrictions are a browser policy. Your server has no idea whether a request came from a page on another origin unless it reads the ",[608,822,823],{},"Origin"," header, and nothing forces it to act on that. ",[608,826,616],{}," does two jobs:",[590,829,830,833,834,837,838,841,842,844,845,848,849,852,853,856,857,859],{},[593,831,832],{},"It answers preflights."," For any request the browser considers non-simple — a ",[608,835,836],{},"PUT",", a ",[608,839,840],{},"Content-Type: application\u002Fjson"," body, a custom header — the browser first sends an ",[608,843,620],{}," request carrying ",[608,846,847],{},"Access-Control-Request-Method"," and ",[608,850,851],{},"Access-Control-Request-Headers",". Starlette's middleware intercepts that before the router sees it and replies from configuration alone. This is why CORS works on routes that only declare ",[608,854,855],{},"@app.post",", and why writing your own ",[608,858,620],{}," handler is unnecessary.",[590,861,862,865,866,868,869,871],{},[593,863,864],{},"It annotates real responses."," For a request with an ",[608,867,823],{}," header, it adds ",[608,870,644],{}," (and friends) to whatever the application returned.",[590,873,874,875,879,880,882],{},"Crucially, when the origin is ",[876,877,878],"em",{},"not"," allowed, the middleware does not block anything. The request runs, your route executes, the response goes out — just without the header. The browser then refuses to expose that response to JavaScript. Your server-side logs show a ",[608,881,610],{},". This is the single biggest source of confusion in CORS debugging, and it is why \"is it a FastAPI bug?\" is almost always answered no.",[662,884,886],{"id":885},"the-configuration","The Configuration",[888,889,894],"pre",{"className":890,"code":891,"language":892,"meta":893,"style":893},"language-python shiki shiki-themes github-light-high-contrast","from fastapi import FastAPI\nfrom fastapi.middleware.cors import CORSMiddleware\n\napp = FastAPI()\n\n# Added last so it ends up outermost: its headers then survive inner middleware errors.\napp.add_middleware(\n    CORSMiddleware,\n    allow_origins=[\"https:\u002F\u002Fapp.example.com\"],\n    allow_credentials=True,\n    allow_methods=[\"GET\", \"POST\"],\n    allow_headers=[\"authorization\", \"content-type\"],\n    max_age=600,\n)\n","python","",[608,895,896,914,927,934,946,951,958,964,970,989,1004,1025,1045,1058],{"__ignoreMap":893},[897,898,900,904,908,911],"span",{"class":749,"line":899},1,[897,901,903],{"class":902},"sTJeM","from",[897,905,907],{"class":906},"sigWx"," fastapi ",[897,909,910],{"class":902},"import",[897,912,913],{"class":906}," FastAPI\n",[897,915,917,919,922,924],{"class":749,"line":916},2,[897,918,903],{"class":902},[897,920,921],{"class":906}," fastapi.middleware.cors ",[897,923,910],{"class":902},[897,925,926],{"class":906}," CORSMiddleware\n",[897,928,930],{"class":749,"line":929},3,[897,931,933],{"emptyLinePlaceholder":932},true,"\n",[897,935,937,940,943],{"class":749,"line":936},4,[897,938,939],{"class":906},"app ",[897,941,942],{"class":902},"=",[897,944,945],{"class":906}," FastAPI()\n",[897,947,949],{"class":749,"line":948},5,[897,950,933],{"emptyLinePlaceholder":932},[897,952,954],{"class":749,"line":953},6,[897,955,957],{"class":956},"sFeEa","# Added last so it ends up outermost: its headers then survive inner middleware errors.\n",[897,959,961],{"class":749,"line":960},7,[897,962,963],{"class":906},"app.add_middleware(\n",[897,965,967],{"class":749,"line":966},8,[897,968,969],{"class":906},"    CORSMiddleware,\n",[897,971,973,977,979,982,986],{"class":749,"line":972},9,[897,974,976],{"class":975},"sV4o_","    allow_origins",[897,978,942],{"class":902},[897,980,981],{"class":906},"[",[897,983,985],{"class":984},"sYEJz","\"https:\u002F\u002Fapp.example.com\"",[897,987,988],{"class":906},"],\n",[897,990,992,995,997,1001],{"class":749,"line":991},10,[897,993,994],{"class":975},"    allow_credentials",[897,996,942],{"class":902},[897,998,1000],{"class":999},"sacAq","True",[897,1002,1003],{"class":906},",\n",[897,1005,1007,1010,1012,1014,1017,1020,1023],{"class":749,"line":1006},11,[897,1008,1009],{"class":975},"    allow_methods",[897,1011,942],{"class":902},[897,1013,981],{"class":906},[897,1015,1016],{"class":984},"\"GET\"",[897,1018,1019],{"class":906},", ",[897,1021,1022],{"class":984},"\"POST\"",[897,1024,988],{"class":906},[897,1026,1028,1031,1033,1035,1038,1040,1043],{"class":749,"line":1027},12,[897,1029,1030],{"class":975},"    allow_headers",[897,1032,942],{"class":902},[897,1034,981],{"class":906},[897,1036,1037],{"class":984},"\"authorization\"",[897,1039,1019],{"class":906},[897,1041,1042],{"class":984},"\"content-type\"",[897,1044,988],{"class":906},[897,1046,1048,1051,1053,1056],{"class":749,"line":1047},13,[897,1049,1050],{"class":975},"    max_age",[897,1052,942],{"class":902},[897,1054,1055],{"class":999},"600",[897,1057,1003],{"class":906},[897,1059,1061],{"class":749,"line":1060},14,[897,1062,1063],{"class":906},")\n",[590,1065,1066,1067,1070,1071,1019,1074,1077,1078,1081,1082,1085,1086,1089,1090,1093],{},"Origins are matched as exact strings including scheme and port. ",[608,1068,1069],{},"https:\u002F\u002Fapp.example.com"," does not match ",[608,1072,1073],{},"https:\u002F\u002Fapp.example.com:8443",[608,1075,1076],{},"http:\u002F\u002Fapp.example.com",", or ",[608,1079,1080],{},"https:\u002F\u002Fwww.app.example.com",". For a set of subdomains use ",[608,1083,1084],{},"allow_origin_regex"," instead, and anchor it — an unanchored pattern like ",[608,1087,1088],{},"https:\u002F\u002F.*\\.example\\.com"," will happily match ",[608,1091,1092],{},"https:\u002F\u002Fevil.com\u002F?x=https:\u002F\u002Fa.example.com"," in some formulations.",[662,1095,1097],{"id":1096},"proving-it-real-headers-for-every-case","Proving It: Real Headers for Every Case",[590,1099,1100,1101,1103],{},"The verification harness cannot send an ",[608,1102,823],{}," header directly, so this example drives three differently-configured applications through an in-process ASGI transport and returns the real CORS headers each one produced.",[888,1105,1107],{"className":890,"code":1106,"language":892,"meta":893,"style":893},"\"\"\"Real CORS headers: preflight, disallowed origin, and allow_credentials with allow_origins=['*'].\"\"\"\nimport httpx\nfrom fastapi import FastAPI\nfrom fastapi.middleware.cors import CORSMiddleware\n\n\ndef build(**cors_kwargs) -> FastAPI:\n    target = FastAPI()\n    target.add_middleware(CORSMiddleware, **cors_kwargs)\n\n    @target.get(\"\u002Fapi\u002Fdata\")\n    async def data():\n        return {\"ok\": True}\n\n    @target.post(\"\u002Fapi\u002Fdata\")\n    async def create():\n        return {\"created\": True}\n\n    return target\n\n\nstrict = build(\n    allow_origins=[\"https:\u002F\u002Fapp.example.com\"],\n    allow_credentials=True,\n    allow_methods=[\"GET\", \"POST\"],\n    allow_headers=[\"authorization\", \"content-type\"],\n    max_age=600,\n)\n\nwildcard_with_credentials = build(\n    allow_origins=[\"*\"], allow_credentials=True, allow_methods=[\"*\"], allow_headers=[\"*\"]\n)\n\nwildcard_no_credentials = build(\n    allow_origins=[\"*\"], allow_credentials=False, allow_methods=[\"*\"], allow_headers=[\"*\"]\n)\n\n\nasync def probe(target: FastAPI, method: str, headers: dict) -> dict:\n    transport = httpx.ASGITransport(app=target)\n    async with httpx.AsyncClient(transport=transport, base_url=\"http:\u002F\u002Fapi.example.com\") as c:\n        response = await c.request(method, \"\u002Fapi\u002Fdata\", headers=headers)\n    return {\n        \"status\": response.status_code,\n        \"cors_headers\": {\n            k: v for k, v in sorted(response.headers.items())\n            if k.startswith(\"access-control-\") or k == \"vary\"\n        },\n    }\n\n\nPREFLIGHT = {\n    \"Origin\": \"https:\u002F\u002Fapp.example.com\",\n    \"Access-Control-Request-Method\": \"POST\",\n    \"Access-Control-Request-Headers\": \"authorization,content-type\",\n}\n",[608,1108,1109,1114,1121,1131,1141,1145,1149,1167,1176,1186,1190,1202,1216,1235,1239,1251,1263,1279,1284,1293,1298,1303,1314,1327,1338,1355,1372,1383,1388,1393,1403,1450,1455,1460,1470,1512,1517,1522,1527,1558,1577,1613,1639,1647,1656,1665,1686,1712,1718,1724,1729,1734,1745,1757,1769,1782],{"__ignoreMap":893},[897,1110,1111],{"class":749,"line":899},[897,1112,1113],{"class":984},"\"\"\"Real CORS headers: preflight, disallowed origin, and allow_credentials with allow_origins=['*'].\"\"\"\n",[897,1115,1116,1118],{"class":749,"line":916},[897,1117,910],{"class":902},[897,1119,1120],{"class":906}," httpx\n",[897,1122,1123,1125,1127,1129],{"class":749,"line":929},[897,1124,903],{"class":902},[897,1126,907],{"class":906},[897,1128,910],{"class":902},[897,1130,913],{"class":906},[897,1132,1133,1135,1137,1139],{"class":749,"line":936},[897,1134,903],{"class":902},[897,1136,921],{"class":906},[897,1138,910],{"class":902},[897,1140,926],{"class":906},[897,1142,1143],{"class":749,"line":948},[897,1144,933],{"emptyLinePlaceholder":932},[897,1146,1147],{"class":749,"line":953},[897,1148,933],{"emptyLinePlaceholder":932},[897,1150,1151,1154,1158,1161,1164],{"class":749,"line":960},[897,1152,1153],{"class":902},"def",[897,1155,1157],{"class":1156},"s3dhs"," build",[897,1159,1160],{"class":906},"(",[897,1162,1163],{"class":902},"**",[897,1165,1166],{"class":906},"cors_kwargs) -> FastAPI:\n",[897,1168,1169,1172,1174],{"class":749,"line":966},[897,1170,1171],{"class":906},"    target ",[897,1173,942],{"class":902},[897,1175,945],{"class":906},[897,1177,1178,1181,1183],{"class":749,"line":972},[897,1179,1180],{"class":906},"    target.add_middleware(CORSMiddleware, ",[897,1182,1163],{"class":902},[897,1184,1185],{"class":906},"cors_kwargs)\n",[897,1187,1188],{"class":749,"line":991},[897,1189,933],{"emptyLinePlaceholder":932},[897,1191,1192,1195,1197,1200],{"class":749,"line":1006},[897,1193,1194],{"class":1156},"    @target.get",[897,1196,1160],{"class":906},[897,1198,1199],{"class":984},"\"\u002Fapi\u002Fdata\"",[897,1201,1063],{"class":906},[897,1203,1204,1207,1210,1213],{"class":749,"line":1027},[897,1205,1206],{"class":902},"    async",[897,1208,1209],{"class":902}," def",[897,1211,1212],{"class":1156}," data",[897,1214,1215],{"class":906},"():\n",[897,1217,1218,1221,1224,1227,1230,1232],{"class":749,"line":1047},[897,1219,1220],{"class":902},"        return",[897,1222,1223],{"class":906}," {",[897,1225,1226],{"class":984},"\"ok\"",[897,1228,1229],{"class":906},": ",[897,1231,1000],{"class":999},[897,1233,1234],{"class":906},"}\n",[897,1236,1237],{"class":749,"line":1060},[897,1238,933],{"emptyLinePlaceholder":932},[897,1240,1242,1245,1247,1249],{"class":749,"line":1241},15,[897,1243,1244],{"class":1156},"    @target.post",[897,1246,1160],{"class":906},[897,1248,1199],{"class":984},[897,1250,1063],{"class":906},[897,1252,1254,1256,1258,1261],{"class":749,"line":1253},16,[897,1255,1206],{"class":902},[897,1257,1209],{"class":902},[897,1259,1260],{"class":1156}," create",[897,1262,1215],{"class":906},[897,1264,1266,1268,1270,1273,1275,1277],{"class":749,"line":1265},17,[897,1267,1220],{"class":902},[897,1269,1223],{"class":906},[897,1271,1272],{"class":984},"\"created\"",[897,1274,1229],{"class":906},[897,1276,1000],{"class":999},[897,1278,1234],{"class":906},[897,1280,1282],{"class":749,"line":1281},18,[897,1283,933],{"emptyLinePlaceholder":932},[897,1285,1287,1290],{"class":749,"line":1286},19,[897,1288,1289],{"class":902},"    return",[897,1291,1292],{"class":906}," target\n",[897,1294,1296],{"class":749,"line":1295},20,[897,1297,933],{"emptyLinePlaceholder":932},[897,1299,1301],{"class":749,"line":1300},21,[897,1302,933],{"emptyLinePlaceholder":932},[897,1304,1306,1309,1311],{"class":749,"line":1305},22,[897,1307,1308],{"class":906},"strict ",[897,1310,942],{"class":902},[897,1312,1313],{"class":906}," build(\n",[897,1315,1317,1319,1321,1323,1325],{"class":749,"line":1316},23,[897,1318,976],{"class":975},[897,1320,942],{"class":902},[897,1322,981],{"class":906},[897,1324,985],{"class":984},[897,1326,988],{"class":906},[897,1328,1330,1332,1334,1336],{"class":749,"line":1329},24,[897,1331,994],{"class":975},[897,1333,942],{"class":902},[897,1335,1000],{"class":999},[897,1337,1003],{"class":906},[897,1339,1341,1343,1345,1347,1349,1351,1353],{"class":749,"line":1340},25,[897,1342,1009],{"class":975},[897,1344,942],{"class":902},[897,1346,981],{"class":906},[897,1348,1016],{"class":984},[897,1350,1019],{"class":906},[897,1352,1022],{"class":984},[897,1354,988],{"class":906},[897,1356,1358,1360,1362,1364,1366,1368,1370],{"class":749,"line":1357},26,[897,1359,1030],{"class":975},[897,1361,942],{"class":902},[897,1363,981],{"class":906},[897,1365,1037],{"class":984},[897,1367,1019],{"class":906},[897,1369,1042],{"class":984},[897,1371,988],{"class":906},[897,1373,1375,1377,1379,1381],{"class":749,"line":1374},27,[897,1376,1050],{"class":975},[897,1378,942],{"class":902},[897,1380,1055],{"class":999},[897,1382,1003],{"class":906},[897,1384,1386],{"class":749,"line":1385},28,[897,1387,1063],{"class":906},[897,1389,1391],{"class":749,"line":1390},29,[897,1392,933],{"emptyLinePlaceholder":932},[897,1394,1396,1399,1401],{"class":749,"line":1395},30,[897,1397,1398],{"class":906},"wildcard_with_credentials ",[897,1400,942],{"class":902},[897,1402,1313],{"class":906},[897,1404,1406,1408,1410,1412,1415,1418,1421,1423,1425,1427,1430,1432,1434,1436,1438,1441,1443,1445,1447],{"class":749,"line":1405},31,[897,1407,976],{"class":975},[897,1409,942],{"class":902},[897,1411,981],{"class":906},[897,1413,1414],{"class":984},"\"*\"",[897,1416,1417],{"class":906},"], ",[897,1419,1420],{"class":975},"allow_credentials",[897,1422,942],{"class":902},[897,1424,1000],{"class":999},[897,1426,1019],{"class":906},[897,1428,1429],{"class":975},"allow_methods",[897,1431,942],{"class":902},[897,1433,981],{"class":906},[897,1435,1414],{"class":984},[897,1437,1417],{"class":906},[897,1439,1440],{"class":975},"allow_headers",[897,1442,942],{"class":902},[897,1444,981],{"class":906},[897,1446,1414],{"class":984},[897,1448,1449],{"class":906},"]\n",[897,1451,1453],{"class":749,"line":1452},32,[897,1454,1063],{"class":906},[897,1456,1458],{"class":749,"line":1457},33,[897,1459,933],{"emptyLinePlaceholder":932},[897,1461,1463,1466,1468],{"class":749,"line":1462},34,[897,1464,1465],{"class":906},"wildcard_no_credentials ",[897,1467,942],{"class":902},[897,1469,1313],{"class":906},[897,1471,1473,1475,1477,1479,1481,1483,1485,1487,1490,1492,1494,1496,1498,1500,1502,1504,1506,1508,1510],{"class":749,"line":1472},35,[897,1474,976],{"class":975},[897,1476,942],{"class":902},[897,1478,981],{"class":906},[897,1480,1414],{"class":984},[897,1482,1417],{"class":906},[897,1484,1420],{"class":975},[897,1486,942],{"class":902},[897,1488,1489],{"class":999},"False",[897,1491,1019],{"class":906},[897,1493,1429],{"class":975},[897,1495,942],{"class":902},[897,1497,981],{"class":906},[897,1499,1414],{"class":984},[897,1501,1417],{"class":906},[897,1503,1440],{"class":975},[897,1505,942],{"class":902},[897,1507,981],{"class":906},[897,1509,1414],{"class":984},[897,1511,1449],{"class":906},[897,1513,1515],{"class":749,"line":1514},36,[897,1516,1063],{"class":906},[897,1518,1520],{"class":749,"line":1519},37,[897,1521,933],{"emptyLinePlaceholder":932},[897,1523,1525],{"class":749,"line":1524},38,[897,1526,933],{"emptyLinePlaceholder":932},[897,1528,1530,1533,1535,1538,1541,1544,1547,1550,1553,1555],{"class":749,"line":1529},39,[897,1531,1532],{"class":902},"async",[897,1534,1209],{"class":902},[897,1536,1537],{"class":1156}," probe",[897,1539,1540],{"class":906},"(target: FastAPI, method: ",[897,1542,1543],{"class":999},"str",[897,1545,1546],{"class":906},", headers: ",[897,1548,1549],{"class":999},"dict",[897,1551,1552],{"class":906},") -> ",[897,1554,1549],{"class":999},[897,1556,1557],{"class":906},":\n",[897,1559,1561,1564,1566,1569,1572,1574],{"class":749,"line":1560},40,[897,1562,1563],{"class":906},"    transport ",[897,1565,942],{"class":902},[897,1567,1568],{"class":906}," httpx.ASGITransport(",[897,1570,1571],{"class":975},"app",[897,1573,942],{"class":902},[897,1575,1576],{"class":906},"target)\n",[897,1578,1580,1582,1585,1588,1591,1593,1596,1599,1601,1604,1607,1610],{"class":749,"line":1579},41,[897,1581,1206],{"class":902},[897,1583,1584],{"class":902}," with",[897,1586,1587],{"class":906}," httpx.AsyncClient(",[897,1589,1590],{"class":975},"transport",[897,1592,942],{"class":902},[897,1594,1595],{"class":906},"transport, ",[897,1597,1598],{"class":975},"base_url",[897,1600,942],{"class":902},[897,1602,1603],{"class":984},"\"http:\u002F\u002Fapi.example.com\"",[897,1605,1606],{"class":906},") ",[897,1608,1609],{"class":902},"as",[897,1611,1612],{"class":906}," c:\n",[897,1614,1616,1619,1621,1624,1627,1629,1631,1634,1636],{"class":749,"line":1615},42,[897,1617,1618],{"class":906},"        response ",[897,1620,942],{"class":902},[897,1622,1623],{"class":902}," await",[897,1625,1626],{"class":906}," c.request(method, ",[897,1628,1199],{"class":984},[897,1630,1019],{"class":906},[897,1632,1633],{"class":975},"headers",[897,1635,942],{"class":902},[897,1637,1638],{"class":906},"headers)\n",[897,1640,1642,1644],{"class":749,"line":1641},43,[897,1643,1289],{"class":902},[897,1645,1646],{"class":906}," {\n",[897,1648,1650,1653],{"class":749,"line":1649},44,[897,1651,1652],{"class":984},"        \"status\"",[897,1654,1655],{"class":906},": response.status_code,\n",[897,1657,1659,1662],{"class":749,"line":1658},45,[897,1660,1661],{"class":984},"        \"cors_headers\"",[897,1663,1664],{"class":906},": {\n",[897,1666,1668,1671,1674,1677,1680,1683],{"class":749,"line":1667},46,[897,1669,1670],{"class":906},"            k: v ",[897,1672,1673],{"class":902},"for",[897,1675,1676],{"class":906}," k, v ",[897,1678,1679],{"class":902},"in",[897,1681,1682],{"class":999}," sorted",[897,1684,1685],{"class":906},"(response.headers.items())\n",[897,1687,1689,1692,1695,1698,1700,1703,1706,1709],{"class":749,"line":1688},47,[897,1690,1691],{"class":902},"            if",[897,1693,1694],{"class":906}," k.startswith(",[897,1696,1697],{"class":984},"\"access-control-\"",[897,1699,1606],{"class":906},[897,1701,1702],{"class":902},"or",[897,1704,1705],{"class":906}," k ",[897,1707,1708],{"class":902},"==",[897,1710,1711],{"class":984}," \"vary\"\n",[897,1713,1715],{"class":749,"line":1714},48,[897,1716,1717],{"class":906},"        },\n",[897,1719,1721],{"class":749,"line":1720},49,[897,1722,1723],{"class":906},"    }\n",[897,1725,1727],{"class":749,"line":1726},50,[897,1728,933],{"emptyLinePlaceholder":932},[897,1730,1732],{"class":749,"line":1731},51,[897,1733,933],{"emptyLinePlaceholder":932},[897,1735,1737,1740,1743],{"class":749,"line":1736},52,[897,1738,1739],{"class":999},"PREFLIGHT",[897,1741,1742],{"class":902}," =",[897,1744,1646],{"class":906},[897,1746,1748,1751,1753,1755],{"class":749,"line":1747},53,[897,1749,1750],{"class":984},"    \"Origin\"",[897,1752,1229],{"class":906},[897,1754,985],{"class":984},[897,1756,1003],{"class":906},[897,1758,1760,1763,1765,1767],{"class":749,"line":1759},54,[897,1761,1762],{"class":984},"    \"Access-Control-Request-Method\"",[897,1764,1229],{"class":906},[897,1766,1022],{"class":984},[897,1768,1003],{"class":906},[897,1770,1772,1775,1777,1780],{"class":749,"line":1771},55,[897,1773,1774],{"class":984},"    \"Access-Control-Request-Headers\"",[897,1776,1229],{"class":906},[897,1778,1779],{"class":984},"\"authorization,content-type\"",[897,1781,1003],{"class":906},[897,1783,1785],{"class":749,"line":1784},56,[897,1786,1234],{"class":906},[1788,1789,1791],"h3",{"id":1790},"a-preflight-that-succeeds","A preflight that succeeds",[888,1793,1797],{"className":1794,"code":1796,"language":717,"meta":893},[1795],"language-text","$ GET \u002Fpreflight-allowed\n200 OK\n{\n  \"status\": 200,\n  \"cors_headers\": {\n    \"access-control-allow-credentials\": \"true\",\n    \"access-control-allow-headers\": \"Accept, Accept-Language, Content-Language, Content-Type, authorization, content-type\",\n    \"access-control-allow-methods\": \"GET, POST\",\n    \"access-control-allow-origin\": \"https:\u002F\u002Fapp.example.com\",\n    \"access-control-max-age\": \"600\",\n    \"vary\": \"Origin\"\n  }\n}\n",[608,1798,1796],{"__ignoreMap":893},[590,1800,1801,1802,1805,1806,1809,1810,1813,1814,1019,1817,1019,1820,1019,1823,1826],{},"The echoed origin is the exact string from ",[608,1803,1804],{},"allow_origins",", and ",[608,1807,1808],{},"vary: Origin"," is present so caches do not serve one origin's response to another. Note that ",[608,1811,1812],{},"allow-headers"," contains four entries you did not configure — ",[608,1815,1816],{},"Accept",[608,1818,1819],{},"Accept-Language",[608,1821,1822],{},"Content-Language",[608,1824,1825],{},"Content-Type"," are the CORS-safelisted request headers, which Starlette always includes.",[1788,1828,1830],{"id":1829},"a-preflight-from-a-disallowed-origin","A preflight from a disallowed origin",[888,1832,1835],{"className":1833,"code":1834,"language":717,"meta":893},[1795],"$ GET \u002Fpreflight-bad-origin\n200 OK\n{\n  \"status\": 400,\n  \"cors_headers\": {\n    \"access-control-allow-credentials\": \"true\",\n    \"access-control-allow-headers\": \"Accept, Accept-Language, Content-Language, Content-Type, authorization, content-type\",\n    \"access-control-allow-methods\": \"GET, POST\",\n    \"access-control-max-age\": \"600\",\n    \"vary\": \"Origin\"\n  }\n}\n",[608,1836,1834],{"__ignoreMap":893},[590,1838,1839,1840,1843,1844,1850,1851,1854],{},"Status ",[608,1841,1842],{},"400",", and — the decisive detail — ",[593,1845,1846,1847,1849],{},"no ",[608,1848,644],{}," header",". Everything else is still there, which is why eyeballing the response in DevTools can mislead: the presence of several ",[608,1852,1853],{},"access-control-*"," headers looks like CORS is working. It is the absence of exactly one that matters.",[1788,1856,1858],{"id":1857},"a-preflight-asking-for-a-header-you-did-not-allow","A preflight asking for a header you did not allow",[888,1860,1863],{"className":1861,"code":1862,"language":717,"meta":893},[1795],"$ GET \u002Fpreflight-bad-header\n200 OK\n{\n  \"status\": 400,\n  \"cors_headers\": {\n    \"access-control-allow-credentials\": \"true\",\n    \"access-control-allow-headers\": \"Accept, Accept-Language, Content-Language, Content-Type, authorization, content-type\",\n    \"access-control-allow-methods\": \"GET, POST\",\n    \"access-control-allow-origin\": \"https:\u002F\u002Fapp.example.com\",\n    \"access-control-max-age\": \"600\",\n    \"vary\": \"Origin\"\n  }\n}\n",[608,1864,1862],{"__ignoreMap":893},[590,1866,1867,1868,1871,1872,1874,1875,1877,1878,1881,1882,1884,1885,1888,1889,1892,1893,1896,1897,1900,1901,1903],{},"Here the origin ",[876,1869,1870],{},"is"," allowed, so ",[608,1873,644],{}," is present, but the status is still ",[608,1876,1842],{}," because ",[608,1879,1880],{},"x-not-allowed"," was requested and is not in ",[608,1883,1440],{},". Starlette returns a plain-text body naming the reason — ",[608,1886,1887],{},"Disallowed CORS headers"," here, and ",[608,1890,1891],{},"Disallowed CORS origin"," in the previous case. If you have a client sending ",[608,1894,1895],{},"x-request-id"," or ",[608,1898,1899],{},"x-api-version"," and never added it to ",[608,1902,1440],{},", this is your failure.",[1788,1905,1907],{"id":1906},"the-real-request-allowed-and-disallowed","The real request, allowed and disallowed",[888,1909,1912],{"className":1910,"code":1911,"language":717,"meta":893},[1795],"$ GET \u002Fsimple-allowed\n200 OK\n{\n  \"status\": 200,\n  \"cors_headers\": {\n    \"access-control-allow-credentials\": \"true\",\n    \"access-control-allow-origin\": \"https:\u002F\u002Fapp.example.com\",\n    \"vary\": \"Origin\"\n  }\n}\n\n$ GET \u002Fsimple-bad-origin\n200 OK\n{\n  \"status\": 200,\n  \"cors_headers\": {\n    \"access-control-allow-credentials\": \"true\"\n  }\n}\n\n$ GET \u002Fno-origin-header\n200 OK\n{\n  \"status\": 200,\n  \"cors_headers\": {}\n}\n",[608,1913,1911],{"__ignoreMap":893},[590,1915,1916,1917,1919],{},"The disallowed origin got a ",[608,1918,610],{},". The route ran. The database was queried. Only the header is missing, and the browser discards the response. This is exactly the situation where a developer sees a successful request in the server log and concludes FastAPI is fine — and it is fine; the configuration is not.",[590,1921,1922,1923,1925,1926,1929],{},"The last case shows that a request with no ",[608,1924,823],{}," header at all gets no CORS headers. Server-to-server clients and ",[608,1927,1928],{},"curl"," are unaffected by any of this.",[1788,1931,1933],{"id":1932},"the-wildcard-plus-credentials-trap","The wildcard-plus-credentials trap",[888,1935,1938],{"className":1936,"code":1937,"language":717,"meta":893},[1795],"$ GET \u002Fwildcard-with-credentials\n200 OK\n{\n  \"status\": 200,\n  \"cors_headers\": {\n    \"access-control-allow-credentials\": \"true\",\n    \"access-control-allow-origin\": \"https:\u002F\u002Fanything.example.com\",\n    \"vary\": \"Origin\"\n  }\n}\n\n$ GET \u002Fwildcard-no-credentials\n200 OK\n{\n  \"status\": 200,\n  \"cors_headers\": {\n    \"access-control-allow-origin\": \"*\"\n  }\n}\n",[608,1939,1937],{"__ignoreMap":893},[590,1941,1942,1943,848,1945,1948,1949,1952,1953,1956],{},"This is the result worth internalising. With ",[608,1944,633],{},[608,1946,1947],{},"allow_credentials=False",", Starlette sends a literal ",[608,1950,1951],{},"*"," and no ",[608,1954,1955],{},"vary"," header — correct, cacheable, and unable to carry cookies.",[590,1958,1959,1960,848,1962,1964,1965,1967,1968,1970,1971,1973,1974,1977,1978,1981,1982,1984,1985,1988,1989,1991],{},"With ",[608,1961,633],{},[608,1963,629],{},", Starlette does ",[593,1966,878],{}," raise an error and does ",[593,1969,878],{}," send ",[608,1972,1951],{},". It echoes back whatever origin asked — here ",[608,1975,1976],{},"https:\u002F\u002Fanything.example.com",", an origin nobody configured — alongside ",[608,1979,1980],{},"allow-credentials: true",". The CORS specification forbids ",[608,1983,1951],{}," with credentials, so Starlette's accommodation is to satisfy the browser by reflecting the origin. The practical effect is that any website on the internet can make credentialed requests to your API with the user's cookies attached and read the response. It is not a crash, it is not a warning, and it will pass every test you write. ",[593,1986,1987],{},"Never combine the two."," Enumerate your origins, or use ",[608,1990,1084],{},".",[662,1993,1995],{"id":1994},"verification","Verification",[590,1997,1998],{},"Reproduce a preflight against a running server without a browser:",[888,2000,2004],{"className":2001,"code":2002,"language":2003,"meta":893,"style":893},"language-bash shiki shiki-themes github-light-high-contrast","curl -i -X OPTIONS https:\u002F\u002Fapi.example.com\u002Fapi\u002Fdata \\\n  -H \"Origin: https:\u002F\u002Fapp.example.com\" \\\n  -H \"Access-Control-Request-Method: POST\" \\\n  -H \"Access-Control-Request-Headers: authorization,content-type\"\n","bash",[608,2005,2006,2025,2035,2044],{"__ignoreMap":893},[897,2007,2008,2010,2013,2016,2019,2022],{"class":749,"line":899},[897,2009,1928],{"class":975},[897,2011,2012],{"class":999}," -i",[897,2014,2015],{"class":999}," -X",[897,2017,2018],{"class":984}," OPTIONS",[897,2020,2021],{"class":984}," https:\u002F\u002Fapi.example.com\u002Fapi\u002Fdata",[897,2023,2024],{"class":902}," \\\n",[897,2026,2027,2030,2033],{"class":749,"line":916},[897,2028,2029],{"class":999},"  -H",[897,2031,2032],{"class":984}," \"Origin: https:\u002F\u002Fapp.example.com\"",[897,2034,2024],{"class":902},[897,2036,2037,2039,2042],{"class":749,"line":929},[897,2038,2029],{"class":999},[897,2040,2041],{"class":984}," \"Access-Control-Request-Method: POST\"",[897,2043,2024],{"class":902},[897,2045,2046,2048],{"class":749,"line":936},[897,2047,2029],{"class":999},[897,2049,2050],{"class":984}," \"Access-Control-Request-Headers: authorization,content-type\"\n",[590,2052,2053,2054,1019,2056,2058,2059,2061,2062,2065,2066,2068],{},"Then check three things in order: the status is ",[608,2055,610],{},[608,2057,644],{}," is present and exactly matches the ",[608,2060,823],{}," you sent, and ",[608,2063,2064],{},"access-control-allow-headers"," covers every header your client sends. If all three hold and the browser still refuses, the request is not reaching your application at all — a proxy, load balancer or CDN in front of it is stripping headers or answering the ",[608,2067,620],{}," itself.",[590,2070,2071],{},"As a regression test:",[888,2073,2075],{"className":890,"code":2074,"language":892,"meta":893,"style":893},"def test_preflight_allows_the_spa(client):\n    r = client.options(\n        \"\u002Fapi\u002Fdata\",\n        headers={\n            \"Origin\": \"https:\u002F\u002Fapp.example.com\",\n            \"Access-Control-Request-Method\": \"POST\",\n            \"Access-Control-Request-Headers\": \"authorization\",\n        },\n    )\n    assert r.headers[\"access-control-allow-origin\"] == \"https:\u002F\u002Fapp.example.com\"\n\n\ndef test_unknown_origin_gets_no_allow_header(client):\n    r = client.get(\"\u002Fapi\u002Fdata\", headers={\"Origin\": \"https:\u002F\u002Fevil.example.com\"})\n    assert \"access-control-allow-origin\" not in r.headers\n",[608,2076,2077,2087,2097,2104,2114,2125,2136,2147,2151,2156,2175,2179,2183,2192,2223],{"__ignoreMap":893},[897,2078,2079,2081,2084],{"class":749,"line":899},[897,2080,1153],{"class":902},[897,2082,2083],{"class":1156}," test_preflight_allows_the_spa",[897,2085,2086],{"class":906},"(client):\n",[897,2088,2089,2092,2094],{"class":749,"line":916},[897,2090,2091],{"class":906},"    r ",[897,2093,942],{"class":902},[897,2095,2096],{"class":906}," client.options(\n",[897,2098,2099,2102],{"class":749,"line":929},[897,2100,2101],{"class":984},"        \"\u002Fapi\u002Fdata\"",[897,2103,1003],{"class":906},[897,2105,2106,2109,2111],{"class":749,"line":936},[897,2107,2108],{"class":975},"        headers",[897,2110,942],{"class":902},[897,2112,2113],{"class":906},"{\n",[897,2115,2116,2119,2121,2123],{"class":749,"line":948},[897,2117,2118],{"class":984},"            \"Origin\"",[897,2120,1229],{"class":906},[897,2122,985],{"class":984},[897,2124,1003],{"class":906},[897,2126,2127,2130,2132,2134],{"class":749,"line":953},[897,2128,2129],{"class":984},"            \"Access-Control-Request-Method\"",[897,2131,1229],{"class":906},[897,2133,1022],{"class":984},[897,2135,1003],{"class":906},[897,2137,2138,2141,2143,2145],{"class":749,"line":960},[897,2139,2140],{"class":984},"            \"Access-Control-Request-Headers\"",[897,2142,1229],{"class":906},[897,2144,1037],{"class":984},[897,2146,1003],{"class":906},[897,2148,2149],{"class":749,"line":966},[897,2150,1717],{"class":906},[897,2152,2153],{"class":749,"line":972},[897,2154,2155],{"class":906},"    )\n",[897,2157,2158,2161,2164,2167,2170,2172],{"class":749,"line":991},[897,2159,2160],{"class":902},"    assert",[897,2162,2163],{"class":906}," r.headers[",[897,2165,2166],{"class":984},"\"access-control-allow-origin\"",[897,2168,2169],{"class":906},"] ",[897,2171,1708],{"class":902},[897,2173,2174],{"class":984}," \"https:\u002F\u002Fapp.example.com\"\n",[897,2176,2177],{"class":749,"line":1006},[897,2178,933],{"emptyLinePlaceholder":932},[897,2180,2181],{"class":749,"line":1027},[897,2182,933],{"emptyLinePlaceholder":932},[897,2184,2185,2187,2190],{"class":749,"line":1047},[897,2186,1153],{"class":902},[897,2188,2189],{"class":1156}," test_unknown_origin_gets_no_allow_header",[897,2191,2086],{"class":906},[897,2193,2194,2196,2198,2201,2203,2205,2207,2209,2212,2215,2217,2220],{"class":749,"line":1060},[897,2195,2091],{"class":906},[897,2197,942],{"class":902},[897,2199,2200],{"class":906}," client.get(",[897,2202,1199],{"class":984},[897,2204,1019],{"class":906},[897,2206,1633],{"class":975},[897,2208,942],{"class":902},[897,2210,2211],{"class":906},"{",[897,2213,2214],{"class":984},"\"Origin\"",[897,2216,1229],{"class":906},[897,2218,2219],{"class":984},"\"https:\u002F\u002Fevil.example.com\"",[897,2221,2222],{"class":906},"})\n",[897,2224,2225,2227,2230,2233,2236],{"class":749,"line":1241},[897,2226,2160],{"class":902},[897,2228,2229],{"class":984}," \"access-control-allow-origin\"",[897,2231,2232],{"class":902}," not",[897,2234,2235],{"class":902}," in",[897,2237,2238],{"class":906}," r.headers\n",[662,2240,2242],{"id":2241},"trade-offs-and-when-not-to","Trade-Offs and When Not To",[590,2244,2245,2248,2249,2252,2253,1991],{},[593,2246,2247],{},"CORS is not authorization."," It restricts which ",[876,2250,2251],{},"web pages"," may read your responses. It does nothing about a server, a script, or a mobile app calling your API. Access control belongs in a dependency, per the reasoning in ",[656,2254,2256],{"href":2255},"\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fmiddleware-vs-dependencies-when-to-use-which\u002F","middleware vs dependencies",[590,2258,2259,2262,2263,2266,2267,2269,2270,2274,2275,2277],{},[593,2260,2261],{},"Ordering matters more than it looks."," If an inner middleware raises and an outer layer converts it to a ",[608,2264,2265],{},"500",", that error response only carries CORS headers if ",[608,2268,616],{}," is outside the failure. Since the last middleware added is the outermost — the rule demonstrated in ",[656,2271,2273],{"href":2272},"\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fmiddleware-execution-order\u002F","middleware execution order"," — add CORS last. Otherwise your frontend reports a CORS error when the real problem is a ",[608,2276,2265],{},", and you debug the wrong thing.",[590,2279,2280,2283,2284,2287,2288,2290,2291,1991],{},[593,2281,2282],{},"Different origins per environment belong in configuration."," Hard-coding ",[608,2285,2286],{},"localhost:3000"," next to your production origin means shipping a permanent local-development hole. Drive ",[608,2289,1804],{}," from settings, as in ",[656,2292,2294],{"href":2293},"\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management\u002Fsecrets-and-env-files-per-environment\u002F","secrets and env files per environment",[590,2296,2297,2303,2304,2306],{},[593,2298,2299,2302],{},[608,2300,2301],{},"max_age"," is a cache, so it is also a delay."," A long ",[608,2305,2301],{}," avoids a preflight per request but means an origin or header change takes that long to take effect in browsers that already cached the answer. Browsers cap it well below most configured values anyway.",[590,2308,2309,2312,2313,2316],{},[593,2310,2311],{},"If you serve the SPA from the same origin, you need none of this."," Putting the API behind ",[608,2314,2315],{},"\u002Fapi"," on the same host removes the cross-origin condition entirely, which is strictly simpler and strictly safer than any configuration on this page.",[662,2318,2320],{"id":2319},"faq","FAQ",[590,2322,2323,2326,2327,2329,2330,2332],{},[593,2324,2325],{},"Why does my browser report a CORS error when curl works fine?","\nBecause CORS is enforced by the browser, not the server. ",[608,2328,1928],{}," ignores the headers entirely, so a request that succeeds there can still be blocked in the browser. The server almost always returned ",[608,2331,610],{},"; the browser refused to hand the response to your JavaScript.",[590,2334,2335,2341],{},[593,2336,2337,2338,2340],{},"Can I use allow_credentials=True with allow_origins=",[897,2339,1414],{},"?","\nThe browser will reject it, because the CORS specification forbids a literal asterisk with credentials. Starlette does not raise an error for this combination; it echoes the requesting origin back instead, which quietly makes every origin a trusted one. List your origins explicitly.",[590,2343,2344,2347,2348,1896,2350,2352,2353,2355,2356,2358],{},[593,2345,2346],{},"Why does my preflight OPTIONS request fail?","\nUsually because the requested method or header is not in ",[608,2349,1429],{},[608,2351,1440],{},". Starlette returns ",[608,2354,1842],{}," with a plain-text reason such as ",[608,2357,1887],{},", and it does not emit an allow-origin header for a disallowed origin, which is what the browser reports.",[590,2360,2361,2364,2365,2367,2368,1896,2371,2374,2375,2377],{},[593,2362,2363],{},"Do I need to add an OPTIONS route to handle preflight?","\nNo. ",[608,2366,616],{}," intercepts preflight requests before routing and answers them itself, which is why it works for routes that only declare ",[608,2369,2370],{},"GET",[608,2372,2373],{},"POST",". Adding your own ",[608,2376,620],{}," handler is unnecessary and can conflict.",[590,2379,2380,2383,2384,2386],{},[593,2381,2382],{},"Where should CORSMiddleware sit in my middleware stack?","\nEffectively outermost, so preflight responses and error responses from inner middleware still carry CORS headers. Since the last middleware added is the outermost, add ",[608,2385,616],{}," last.",[590,2388,2389,2392,2393,2396,2397,2401,2402,1991],{},[593,2390,2391],{},"Why can my JavaScript not read a custom response header?","\nResponse headers other than the safelisted few are hidden from script unless you name them in ",[608,2394,2395],{},"expose_headers",". A correlation ID returned by your ",[656,2398,2400],{"href":2399},"\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fimplementing-custom-middleware-for-request-tracing\u002F","tracing middleware"," is invisible to the frontend until you add ",[608,2403,2404],{},"expose_headers=[\"x-request-id\"]",[662,2406,2408],{"id":2407},"related-reading","Related Reading",[597,2410,2411,2419,2427,2434,2442],{},[600,2412,2413,641,2416,1991],{},[593,2414,2415],{},"Up to the section:",[656,2417,2418],{"href":658},"Middleware Implementation",[600,2420,2421,641,2424,1991],{},[593,2422,2423],{},"Why CORS must be added last:",[656,2425,2426],{"href":2272},"Middleware Execution Order",[600,2428,2429,641,2432,1991],{},[593,2430,2431],{},"Where access control actually belongs:",[656,2433,521],{"href":2255},[600,2435,2436,641,2439,1991],{},[593,2437,2438],{},"Per-environment origin lists:",[656,2440,2441],{"href":2293},"Secrets and Env Files per Environment",[600,2443,2444,641,2447,1991],{},[593,2445,2446],{},"Exposing your trace header to the browser:",[656,2448,509],{"href":2399},[2450,2451,2452],"style",{},"html pre.shiki code .sTJeM, html code.shiki .sTJeM{--shiki-default:#A0111F}html pre.shiki code .sigWx, html code.shiki .sigWx{--shiki-default:#0E1116}html pre.shiki code .sFeEa, html code.shiki .sFeEa{--shiki-default:#66707B}html pre.shiki code .sV4o_, html code.shiki .sV4o_{--shiki-default:#702C00}html pre.shiki code .sYEJz, html code.shiki .sYEJz{--shiki-default:#032563}html pre.shiki code .sacAq, html code.shiki .sacAq{--shiki-default:#023B95}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .s3dhs, html code.shiki .s3dhs{--shiki-default:#622CBC}",{"title":893,"searchDepth":916,"depth":916,"links":2454},[2455,2456,2457,2458,2465,2466,2467,2468],{"id":664,"depth":916,"text":665},{"id":816,"depth":916,"text":817},{"id":885,"depth":916,"text":886},{"id":1096,"depth":916,"text":1097,"children":2459},[2460,2461,2462,2463,2464],{"id":1790,"depth":929,"text":1791},{"id":1829,"depth":929,"text":1830},{"id":1857,"depth":929,"text":1858},{"id":1906,"depth":929,"text":1907},{"id":1932,"depth":929,"text":1933},{"id":1994,"depth":916,"text":1995},{"id":2241,"depth":916,"text":2242},{"id":2319,"depth":916,"text":2320},{"id":2407,"depth":916,"text":2408},"2026-07-20","Configure CORSMiddleware correctly: how preflight works, why allow_credentials with a wildcard origin is unsafe, and how to read the real CORS headers.","md",[2473,2475,2478,2480,2482],{"q":2325,"a":2474},"Because CORS is enforced by the browser, not the server. curl ignores the headers entirely, so a request that succeeds there can still be blocked in the browser. The server almost always returned 200; the browser refused to hand the response to your JavaScript.",{"q":2476,"a":2477},"Can I use allow_credentials=True with allow_origins=['*']?","The browser will reject it, because the CORS specification forbids a literal asterisk with credentials. Starlette does not raise an error for this combination; it echoes the requesting origin back instead, which quietly makes every origin a trusted one. List your origins explicitly.",{"q":2346,"a":2479},"Usually because the requested method or header is not in allow_methods or allow_headers. Starlette returns 400 with a plain-text reason such as Disallowed CORS headers, and it does not emit an allow-origin header for a disallowed origin, which is what the browser reports.",{"q":2363,"a":2481},"No. CORSMiddleware intercepts preflight requests before routing and answers them itself, which is why it works for routes that only declare GET or POST. Adding your own OPTIONS handler is unnecessary and can conflict.",{"q":2382,"a":2483},"Effectively outermost, so preflight responses and error responses from inner middleware still carry CORS headers. Since the last middleware added is the outermost, add CORSMiddleware last.",null,{"slug":2486,"breadcrumb":2487},"cors-middleware-configuration",[2488,2491,2494,2495],{"label":2489,"path":2490},"Home","\u002F",{"label":2492,"path":2493},"Core Architecture & Routing Patterns","\u002Fcore-architecture-routing-patterns\u002F",{"label":2418,"path":658},{"label":2496,"path":2497},"CORS Middleware Configuration","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fcors-middleware-configuration\u002F",{"title":503,"description":2470},"article","RIRtsXW5fgyr4vme6b_gfKZ3tt1jLTo7aXIrfCnS_HE",[2484,2484],1784588202739]