# Configuration: conditionals and variables

This page describes how to define and use conditional matchers in Ferron configuration. Named matchers let you apply configuration selectively based on request properties. The matcher system is part of the `http-server` module's request resolution pipeline.

## Named matchers

Named matchers are declared with `match <name> { ... }` and referenced by `if <name> { ... }` or `if_not <name> { ... }` inside host blocks.

```ferron
match curl_client {
    request.header.user_agent ~ "curl"
}

example.com {
    if curl_client {
        # Configuration for curl clients
    }
}
```

Language matching example:

```ferron
match english_language {
    "en" in request.header.accept_language
}

example.com {
    if english_language {
        root "/var/www/english"
    }
}
```

## Operators

The following operators are available inside `match` blocks:

| Operator | Meaning |
| --- | --- |
| `==` | String equality |
| `!=` | String inequality |
| `~` | Regex match |
| `!~` | Negated regex match |
| `in` | Left value must equal one of the comma-separated items in the right value, or match a language in an `Accept-Language` header |

Notes:

- `in` splits the right-hand string on commas and trims each item.
- When the right value looks like an `Accept-Language` header (contains quality values or multiple language ranges), `in` performs language matching with support for base language codes (e.g. `en` matches `en-US`).
- All expressions inside a single `match` block must pass (AND semantics).

## Built-in variables

The HTTP resolver exposes these variables for use in `match` blocks and interpolation (`{{...}}`):

| Variable | Value |
| --- | --- |
| `request.method` | HTTP method (e.g. `GET`, `POST`) |
| `request.uri.path` | Request path |
| `request.uri.query` | Query string, or empty string |
| `request.uri` | Full request URI |
| `request.version` | HTTP version string (e.g. `HTTP/1.1`) |
| `request.header.<name>` | Request header value |
| `request.host` | Resolved request hostname |
| `request.scheme` | `http` or `https` |
| `request.path_info` | Extra path info after a script match (e.g. `/test` in `/index.php/test`), or empty |
| `server.ip` | Local listener IP address |
| `server.port` | Local listener port |
| `remote.ip` | Client IP address |
| `remote.port` | Client port |

Header names are normalized by lowercasing them and converting `_` to `-`. For example, `request.header.x_forwarded_for` reads the `x-forwarded-for` header.

### IP canonicalization

The `server.ip` and `remote.ip` variables automatically canonicalize IPv4-mapped IPv6 addresses (`::ffff:x.x.x.x`) to their IPv4 form. For example, if the client connects via `::ffff:192.0.2.1`, `remote.ip` returns `192.0.2.1`.

## Interpolated strings

Interpolated strings use `{{name}}` syntax:

- `{{env.NAME}}` reads the `NAME` environment variable.
- Other interpolation variables depend on the consumer of that directive.
- If a variable cannot be resolved, the placeholder is kept as `{{name}}`.

For startup-only TLS settings such as `cert` and `key`, the bundled `manual` TLS provider relies on plain strings or `env.*` interpolation.

## Related directives

- [`if`](https://ferron.sh/docs/v3/configuration/routing-url-processing#if) — applies a block when a named matcher evaluates to true
- [`if_not`](https://ferron.sh/docs/v3/configuration/routing-url-processing#if_not) — applies a block when a named matcher evaluates to false
- [`location`](https://ferron.sh/docs/v3/configuration/routing-url-processing#path-matching) — path-based matching

## Notes and troubleshooting

- All expressions inside a single `match` block must pass (AND semantics).
- If a variable cannot be resolved, the placeholder is kept as `{{name}}`.
- For URL rewriting with regex, see [URL rewriting](https://ferron.sh/docs/v3/configuration/http-rewrite).
- For HTTP response control with regex matching, see [HTTP response control](https://ferron.sh/docs/v3/configuration/http-response).