## Disable Introspection

GraphQL introspection is a powerful feature that allows clients to query a GraphQL server for information about the schema itself, including details about types, queries, mutations, and the overall structure of the API. However, in a production environment, this feature is often not used and poses a potential security risk, as it exposes your API schema to the public. Disabling introspection is recommended to enhance security. By default introspection is enabled. The following configuration should be applied:

```yaml
introspection:
  enabled: false
```

## Disable Development Mode

The development mode enables certain configuration for a better developer experience. Beside pretty logs it enables [ART](https://cosmo-docs.wundergraph.com/router/advanced-request-tracing-art) for local development. ART can expose sensitive information and should never be used in a production environment without Cosmo Studio. By default the development mode is disabled. The following configuration should be applied:

```yaml
dev_mode: false
```

## Disable File Uploads

The router implements the spec [https://github.com/jaydenseric/graphql-multipart-request-spec](https://github.com/jaydenseric/graphql-multipart-request-spec), that defines an interoperable multipart form field structure for GraphQL requests. Due to the nature of file uploads, unnecessary pressure may be placed on the router. By default file uploads are enabled. The following configuration should be applied:

```yaml
file_upload:
    enabled: false
```

## Enable Config Signing

The router configuration contains rules and settings for how your supergraph is executed. You can either allow the router to poll the configuration automatically or deploy the configuration with each router deployment. In both cases, you must trust the origin that provides the configuration. To prevent configuration tampering, we have implemented a feature called [Config-Signing](https://cosmo-docs.wundergraph.com/router/security/config-validation-and-signing). By default every unsigned config can be used. The following configuration should be applied after signing has been activated:

```yaml
graph:
  sign_key: 'sign_key' # or GRAPH_CONFIG_SIGN_KEY
```

## Enable Rate Limiting

The router can protect your subgraphs from overloading by implementing a [GCRA](https://en.wikipedia.org/wiki/Generic_cell_rate_algorithm) (leaky bucket) rate limiter based on Redis. We encourage everyone to use it unless you already have protection in place. Before applying it to production, please test it thoroughly. We recommend setting higher limits initially to avoid any interruptions. By default rate limiting is disabled. The following configuration should be applied:

```yaml
rate_limit:
  enabled: true
  storage:
    cluster_enabled: false # set to true to use a Redis Cluster
    urls:
     - redis://localhost:6379
    key_prefix: cosmo_rate_limit
  simple_strategy:
    rate: 100
    burst: 200
    period: 1s
```

Prior to [router@0.169.0](https://github.com/wundergraph/cosmo/releases/tag/router%400.168.1), the redis configuration looks like:

```yaml
rate_limit:
   storage:
    url: redis://localhost:6379
    key_prefix: cosmo_rate_limit
```

### Per-Key Rate Limit Overrides

You can apply different rate limits to specific keys by defining overrides under `simple_strategy`. Each override includes a `matching` field, a [Go regex](https://pkg.go.dev/regexp/syntax) tested against the suffix portion of the rate-limit key (the result of `key_suffix_expression`), not the full prefixed Redis key. The first matching override wins; unmatched keys fall back to the global defaults. This can be useful when different consumers of your API need different throughput allowances. For example, you might assign a restrictive limit to LLM-based clients that generate high query volumes, a moderate limit to external API consumers, and a permissive limit to internal services:

```yaml
rate_limit:
  enabled: true
  storage:
    cluster_enabled: false
    urls:
      - redis://localhost:6379
    key_prefix: cosmo_rate_limit
  simple_strategy:
    rate: 100
    burst: 200
    period: 1s
    key_suffix_expression: "request.auth.claims.consumer_type"
    overrides:
      - matching: "^mcp$"
        rate: 20
        burst: 30
        period: 1s
      - matching: "^internal$"
        rate: 5000
        burst: 10000
        period: 1s
```

In this configuration, requests with a JWT `consumer_type` claim of `mcp` are limited to 20 req/s, those with `internal` get 5000 req/s, and all other consumers fall back to the default 100 req/s.

## Configure CORS

If you don’t have an intermediate proxy handling CORS requests, we recommend allowing only the origins you trust. This also applies to headers and methods. By default all origins are allowed. The following configuration should be applied:

```yaml
cors:
  allow_methods:
    - "POST"
    - "GET"
  allow_origins:
    - "mydomain.com"
    - "*.mydomain.com:*/api"
  allow_headers:
    - "Authorization"
  max_age: 5m
  allow_credentials: true
```

## Configure subgraph overrides

If your router operates in the same network as your subgraphs, we recommend overwriting the URLs in the router configuration to avoid unnecessary roundtrips and potential attack vectors. By default the subgraph routing URL from the [wgc subgraph create](https://cosmo-docs.wundergraph.com/cli/subgraph/create) is used. The subgraph name needs to match with the name from the command. The following configuration should be applied:

```yaml
override_routing_url:
  subgraphs:
    "subgraph1": "http://localhost:8000"
```

## Leverage persistent operations

[Persistent operations](https://cosmo-docs.wundergraph.com/router/persisted-queries/persisted-operations) are a great way to save bandwidth but also to reduce the attack vectors by only allowing known queries to be executed. We recommend enabling the [PQL manifest](https://cosmo-docs.wundergraph.com/router/persisted-queries/persisted-operations#pql-manifest) to load all persisted operations at startup and serve them from memory. This eliminates per-request network calls and ensures the router has a complete, authoritative view of all allowed operations.

```yaml
persisted_operations:
  manifest:
    enabled: true
security:
  block_non_persisted_operations:
    enabled: true
```

We recommend reviewing the [Persisted Operations](https://cosmo-docs.wundergraph.com/router/persisted-queries/persisted-operations) page to learn more about the different levels of security you can enforce.

## Disable Subscriptions / Mutation if not used

Ideally, you should never expose more than necessary. Sometimes this is unavoidable or you want to enforce it for security reasons. In that case, you can disable subscriptions or mutation to avoid any surprises.

```yaml
security:
  block_subscriptions:
    enabled: true
  block_mutations:
    enabled: true
```

## Enable TLS and HTTP/2

To ensure a secure connection between your load balancer and router, you can enable [TLS](https://cosmo-docs.wundergraph.com/router/security/tls). When TLS is configured and your load balancer supports HTTP/2, all requests are upgraded. This enables all the benefits of the protocol, such as multiplexing. By default TLS is not used. The following configuration should be applied:

```yaml
tls:
  server:
    enabled: true
    key_file: ../your/key.pem
    cert_file: ../your/cert.pem
```

## Log Level

When deploying GraphQL services in production, it’s crucial to manage logging effectively to balance between capturing essential information and minimizing performance overhead and storage consumption. For those reasons, we recommend to use the log level `ERROR` in production. By default the level is **INFO**. The following configuration should be applied:

```yaml
log_level: "error"
```
