# How to Configure CORS Settings for Cross-Origin Requests in AxonHub

> Learn how to configure CORS settings for cross-origin requests in AxonHub. Easily enable and validate CORS with simple server configuration for seamless API integration.

- Repository: [Loop/axonhub](https://github.com/looplj/axonhub)
- Tags: how-to-guide
- Published: 2026-03-06

---

**AxonHub enables cross-origin resource sharing (CORS) through a declarative configuration block under `server.cors`, which activates Gin's CORS middleware when `enabled: true` and validates that `allowed_origins` is non-empty at startup.**

AxonHub is an open-source platform built on the Gin web framework that handles HTTP traffic for event-driven architectures. To configure CORS settings for cross-origin requests, administrators modify Viper-backed configuration files or environment variables that populate the `CORS` struct defined in the server's configuration schema.

## Understanding AxonHub's CORS Architecture

AxonHub implements CORS using the `gin-contrib/cors` middleware. The configuration lifecycle follows three distinct stages: default value definition in [`conf/conf.go`](https://github.com/looplj/axonhub/blob/main/conf/conf.go), runtime validation in [`cmd/axonhub/main.go`](https://github.com/looplj/axonhub/blob/main/cmd/axonhub/main.go), and middleware registration in [`internal/server/routes.go`](https://github.com/looplj/axonhub/blob/main/internal/server/routes.go). This separation ensures that invalid CORS configurations fail fast during application startup rather than at runtime.

## Configuration Schema and Default Values

The CORS configuration struct is initialized with secure defaults in [[`conf/conf.go`](https://github.com/looplj/axonhub/blob/main/conf/conf.go)](https://github.com/looplj/axonhub/blob/unstable/conf/conf.go#L42-L52). The relevant fields include:

- **enabled** – Boolean toggle to activate the middleware (default: `false`).
- **debug** – Enables verbose CORS logging for troubleshooting (default: `false`).
- **allowed_origins** – Slice of permitted origin domains (default: empty).
- **allowed_methods** – HTTP methods allowed for cross-origin requests (default: `["GET", "POST", "PUT", "PATCH", "DELETE"]`).
- **allowed_headers** – Headers browsers may send (default: `["Origin", "Content-Type", "Accept", "Authorization"]`).
- **exposed_headers** – Response headers exposed to the client (default: empty).
- **allow_credentials** – Whether to expose cookies and authorization headers (default: `true`).
- **max_age** – Duration string for preflight cache (default: `"12h"`).

## Enabling CORS Middleware

When the server initializes its Gin engine in [[`internal/server/routes.go`](https://github.com/looplj/axonhub/blob/main/internal/server/routes.go)](https://github.com/looplj/axonhub/blob/unstable/internal/server/routes.go#L59-L72), it checks `server.Config.CORS.Enabled`. If true, it constructs a `cors.Config` object mapping the configuration fields to the middleware options and registers it globally using `server.Use(cors.New(corsConfig))`. Additionally, an explicit `server.OPTIONS("*any", corsHandler)` route handles preflight requests for all endpoints.

## Validation and Startup Checks

To prevent runtime misconfigurations, [[`cmd/axonhub/main.go`](https://github.com/looplj/axonhub/blob/main/cmd/axonhub/main.go)](https://github.com/looplj/axonhub/blob/unstable/cmd/axonhub/main.go#L14-L16) validates that `allowed_origins` contains at least one entry when CORS is enabled. If validation fails, the application exits immediately with a descriptive error message, ensuring that cross-origin policies are explicitly defined before the server accepts traffic.

## Practical Configuration Examples

### YAML Configuration File

Create or modify [`config.yml`](https://github.com/looplj/axonhub/blob/main/config.yml) in the working directory:

```yaml
server:
  cors:
    enabled: true
    debug: false
    allowed_origins:
      - "https://app.example.com"
      - "https://admin.example.com"
    allowed_methods:
      - GET
      - POST
      - PUT
      - DELETE
      - OPTIONS
    allowed_headers:
      - Origin
      - Content-Type
      - Accept
      - Authorization
      - X-Request-ID
    exposed_headers:
      - X-Request-ID
    allow_credentials: true
    max_age: "24h"

```

### Environment Variables

For containerized deployments, export variables following the Viper convention (replace dots with underscores and uppercase):

```bash
export SERVER_CORS_ENABLED=true
export SERVER_CORS_ALLOWED_ORIGINS='["https://app.example.com","http://localhost:3000"]'
export SERVER_CORS_ALLOW_CREDENTIALS=true
export SERVER_CORS_MAX_AGE="12h"
axonhub            # start the server

```

### Programmatic Access (Go)

If you need to inspect the loaded configuration within a plugin or extension:

```go
package main

import (
    "fmt"
    "github.com/looplj/axonhub/conf"
)

func main() {
    cfg, err := conf.Load()
    if err != nil {
        panic(err)
    }
    
    if cfg.APIServer.CORS.Enabled {
        fmt.Printf("CORS enabled for origins: %v\n", cfg.APIServer.CORS.AllowedOrigins)
    }
}

```

## Summary

- AxonHub’s CORS behavior is controlled by the `server.cors` configuration block defined in [[`conf/conf.go`](https://github.com/looplj/axonhub/blob/main/conf/conf.go)](https://github.com/looplj/axonhub/blob/unstable/conf/conf.go#L42-L52).
- The middleware is registered in [[`internal/server/routes.go`](https://github.com/looplj/axonhub/blob/main/internal/server/routes.go)](https://github.com/looplj/axonhub/blob/unstable/internal/server/routes.go#L59-L72) only when `enabled: true`.
- Startup validation in [[`cmd/axonhub/main.go`](https://github.com/looplj/axonhub/blob/main/cmd/axonhub/main.go)](https://github.com/looplj/axonhub/blob/unstable/cmd/axonhub/main.go#L14-L16) ensures `allowed_origins` is populated before the server starts.
- You can configure CORS via YAML, environment variables, or programmatically using Viper-backed settings.

## Frequently Asked Questions

### What happens if I enable CORS but leave `allowed_origins` empty?

The application will exit immediately during startup with a validation error. The check in [[`cmd/axonhub/main.go`](https://github.com/looplj/axonhub/blob/main/cmd/axonhub/main.go)](https://github.com/looplj/axonhub/blob/unstable/cmd/axonhub/main.go#L14-L16) requires at least one origin when CORS is enabled to prevent accidental open CORS policies.

### Can I use wildcards in `allowed_origins`?

The underlying `gin-contrib/cors` library supports the wildcard `"*"` to allow any origin, but this is discouraged for production environments that use credentials. AxonHub’s configuration accepts the standard string slice, so you can include `"*"` as an element, but ensure `allow_credentials` is set to `false` when doing so.

### How do I debug CORS issues?

Set `server.cors.debug: true` in your configuration file or `SERVER_CORS_DEBUG=true` as an environment variable. This enables verbose logging from the Gin CORS middleware, printing allowed origins, methods, and headers to the console for every preflight and actual request.

### Does AxonHub support dynamic CORS configuration without restarting?

Currently, the CORS configuration is loaded once at startup via Viper and passed to the Gin engine during route initialization. Changing CORS settings requires a server restart to re-read the configuration file or environment variables. For dynamic updates, you would need to implement a custom middleware that reads from a hot-reloadable source.