# Configuration: SCGI support

> Source: https://ferron.sh/docs/configuration/content/scgi

This page documents the `scgi` directive for configuring Ferron's SCGI (Simple Common Gateway Interface) support. SCGI is a protocol for interfacing external application servers with web servers. It is similar to CGI but with binary framing for better performance.

## `scgi`

> [!note]
> Host isolation: a named host does not inherit `scgi` from the wildcard `*` host. It still inherits from global defaults, and a `location` block inherits it from its own host.

```ferron
example.com {
    scgi {
        backend tcp://127.0.0.1:4000
        environment APP_ENV "production"
    }
}
```

The `scgi` directive enables SCGI protocol support. When specified, Ferron forwards requests to the configured SCGI backend using the SCGI protocol instead of spawning local processes.

| Form                         | Description                                     |
| ---------------------------- | ----------------------------------------------- |
| `scgi { ... }`               | Enables SCGI and configures nested directives.  |
| `scgi <url: string> { ... }` | Enables SCGI and sets the backend URL directly. |

### `backend`

| Nested directive | Arguments       | Description                                                                                                                                  | Default |
| ---------------- | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
| `backend`        | `<url: string>` | This directive specifies the SCGI backend server URL. Supports TCP URLs (`tcp://host:port`) and Unix socket URLs (`unix:///path/to/socket`). | none    |

**Configuration example:**

```ferron
example.com {
    scgi {
        backend tcp://127.0.0.1:4000
    }
}
```

**Configuration example with Unix socket:**

```ferron
example.com {
    scgi {
        backend unix:///var/run/app.sock
    }
}
```

> [!note]
>
> - TCP URLs must include both host and port (for example, `tcp://127.0.0.1:4000`).
> - Unix socket paths must be absolute paths.
> - When a connection failure occurs (connection refused, host unreachable, and so on), Ferron logs an error and returns a `503 Service Unavailable` response.

### `environment`

| Nested directive | Arguments                        | Description                                                                                                                                                                                 | Default |
| ---------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
| `environment`    | `<name: string> <value: string>` | This directive sets an SCGI environment variable passed to the backend server. Values use the same interpolation syntax as other directives. You can specify this directive multiple times. | none    |

**Configuration example:**

```ferron
example.com {
    scgi {
        backend tcp://127.0.0.1:4000
        environment APP_ENV "production"
        environment APP_SECRET "{{env.APP_SECRET}}"
        environment RUBY_VERSION "3.3"
    }
}
```

> [!note]
>
> - Environment variables take precedence over any existing variables with the same name.
> - The `Proxy` header is automatically removed from the request to prevent the [httpoxy](https://httpoxy.org/) vulnerability.
> - Ferron always sets `SERVER_SOFTWARE`, `SERVER_NAME`, `SERVER_ADDR`, `SERVER_PORT`, `REQUEST_URI`, `QUERY_STRING`, `PATH_INFO`, `SCRIPT_NAME`, `AUTH_TYPE`, `REMOTE_USER`, and `SERVER_ADMIN` automatically.

## Environment variables

Ferron automatically sets the following SCGI environment variables:

| Variable          | Description                                                                           |
| ----------------- | ------------------------------------------------------------------------------------- |
| `SERVER_SOFTWARE` | Always `Ferron`.                                                                      |
| `SERVER_NAME`     | Server hostname.                                                                      |
| `SERVER_ADDR`     | Local server address.                                                                 |
| `SERVER_PORT`     | Server port.                                                                          |
| `REQUEST_METHOD`  | HTTP method.                                                                          |
| `REQUEST_URI`     | Original request URI.                                                                 |
| `QUERY_STRING`    | Query string (empty string if none).                                                  |
| `PATH_INFO`       | Path info extracted from the request.                                                 |
| `SCRIPT_NAME`     | The script path relative to the document root.                                        |
| `AUTH_TYPE`       | Authentication type from the `Authorization` header (for example, `Basic`, `Bearer`). |
| `REMOTE_USER`     | Authenticated username, if available.                                                 |
| `SERVER_ADMIN`    | Server administrator email (from `admin_email` configuration).                        |
| `HTTPS`           | Set to `on` when the connection uses encryption.                                      |

Additional variables set by `environment` directives override any automatically set variables with the same name.

> [!tip]
> For authentication integration, SCGI scripts receive `REMOTE_USER` and `AUTH_TYPE` only when used alongside a module like `http-basicauth` that sets `ctx.auth_user`.

## Authentication

When used alongside an authentication module (for example, `http-basicauth`), Ferron automatically populates the `AUTH_TYPE` and `REMOTE_USER` environment variables in the SCGI request. Ferron extracts the authentication type from the `Authorization` header (for example, `Basic` or `Bearer`).

## Trace context injection

When a trace context exists for the request, Ferron automatically injects W3C Trace Context headers (`traceparent`, `tracestate`, and `baggage`) into the SCGI request. The server maps these headers to standard CGI environment variables:

| Header        | SCGI environment variable |
| ------------- | ------------------------- |
| `traceparent` | `HTTP_TRACEPARENT`        |
| `tracestate`  | `HTTP_TRACESTATE`         |
| `baggage`     | `HTTP_BAGGAGE`            |

This enables end-to-end distributed tracing with SCGI backend applications.

> [!info]
> No per-module configuration is necessary. The server controls trace context injection globally based on whether a trace context exists. See [Tracing configuration](https://ferron.sh/docs/configuration/observability/tracing.md) for details on enabling trace generation and sampling.

## Observability

### Logs

- **`ERROR`**: logged when a connection to the SCGI backend fails. The message includes the connection error details.

### Structured logs

| Description (summary)    | Level | Attributes                                      |
| ------------------------ | ----- | ----------------------------------------------- |
| SCGI service unavailable | ERROR | `upstream.address` (string): backend server URL |

### Metrics

| Metric                          | Type      | Attributes                                                        | Description                                                                |
| ------------------------------- | --------- | ----------------------------------------------------------------- | -------------------------------------------------------------------------- |
| `ferron.scgi.requests`          | Counter   | None                                                              | Number of SCGI requests processed                                          |
| `ferron.scgi.failures`          | Counter   | `error.type` (`"service_unavailable"`), `ferron.scgi.backend_url` | Number of SCGI requests that failed before the backend returned a response |
| `ferron.scgi.upstream.duration` | Histogram | `ferron.scgi.backend_url`                                         | Duration of SCGI upstream request processing                               |

### Access log fields

The SCGI module contributes the following field to the HTTP access log line:

| Field                     | Type   | Description       |
| ------------------------- | ------ | ----------------- |
| `ferron.scgi.backend_url` | string | SCGI backend URL. |

### Trace spans

The SCGI stage sets the following attributes on its `ferron.stage.scgi` span:

| Attribute                   | Type   | Description                                            |
| --------------------------- | ------ | ------------------------------------------------------ |
| `http.response.status_code` | int    | HTTP status code returned by the SCGI backend.         |
| `ferron.scgi.backend_url`   | string | URL of the SCGI backend.                               |
| `error.type`                | string | Error type on failure, enabling trace UI highlighting. |

## Examples

### Basic SCGI backend

```ferron
example.com {
    scgi tcp://127.0.0.1:4000
}
```

### SCGI with Unix socket

```ferron
example.com {
    scgi unix:///var/run/app.sock
}
```

### SCGI with environment variables

```ferron
example.com {
    scgi {
        backend tcp://127.0.0.1:4000
        environment APP_ENV "production"
        environment APP_SECRET "{{env.APP_SECRET}}"
        environment RUBY_VERSION "3.3"
    }
}
```

### SCGI with authentication

```ferron
example.com {
    root /var/www/html

    basic_auth {
        users {
            admin "$argon2id$v=19$m=19456,t=2,p=1$..."
        }
    }

    scgi tcp://127.0.0.1:4000
}
```