# How @id Fields Work in Caddy Config: Complete Guide to Meta-Field Indexing

> Understand how @id fields act as meta fields in Caddy config, assigning stable identifiers and creating admin API shortcuts before stripping them for runtime.

- Repository: [Caddy/caddy](https://github.com/caddyserver/caddy)
- Tags: deep-dive
- Published: 2026-03-03

---

**Caddy treats `@id` as a meta-field that assigns stable identifiers to JSON objects, enabling convenient `/id/<identifier>` admin API shortcuts while stripping the field before runtime configuration loading.**

Caddy's configuration system uses `@id` fields to create durable references to configuration objects without polluting the final `Config` structure. According to the caddyserver/caddy source code, these meta-fields enable stable addressing through the admin API while remaining transparent to module provisioning and validation. Understanding how Caddy @id fields work is essential for managing complex deployments programmatically.

## What Are @id Fields in Caddy Configuration?

In the Caddy architecture, an `@id` field acts as a **meta-field** that can be attached to any JSON object within a configuration. The field serves three primary purposes: it assigns a stable identifier to config objects, builds an in-memory index mapping identifiers to their JSON paths, and exposes a shorthand admin API endpoint at `/id/<identifier>`.

Critically, `@id` does not become part of the final runtime `Config` structure. The field is stripped during the loading phase so it never interferes with module provisioning or validation.

## The @id Field Lifecycle

The transformation of `@id` from configuration text to usable API reference follows a strict pipeline across two core files: [`caddy.go`](https://github.com/caddyserver/caddy/blob/main/caddy.go) and [`admin.go`](https://github.com/caddyserver/caddy/blob/main/admin.go).

### Loading and Meta-Field Stripping

When Caddy loads configuration, it first sanitizes the raw JSON to remove meta-fields before unmarshaling. The `RemoveMetaFields` function in **[`admin.go`](https://github.com/caddyserver/caddy/blob/main/admin.go)** (line 1325) handles this preprocessing.

This function uses a pre-compiled regular expression defined as `idRegexp` in **[`admin.go`](https://github.com/caddyserver/caddy/blob/main/admin.go)** (line 1444) to match and remove `@id` entries and their surrounding commas. This stripping prevents the JSON decoder from encountering unknown fields that would cause unmarshaling errors.

### Configuration Parsing

After meta-field removal, the cleaned JSON proceeds to `unsyncedDecodeAndRun` in **[`caddy.go`](https://github.com/caddyserver/caddy/blob/main/caddy.go)** (lines 313-324). Here, the sanitized configuration is unmarshaled into a `*Config` structure. Because `@id` has already been removed, the parser encounters only valid configuration fields recognized by Caddy's module system.

### Index Building

Once loaded, Caddy recursively traverses the configuration to build an addressable index. The `indexConfigObjects` function in **[`caddy.go`](https://github.com/caddyserver/caddy/blob/main/caddy.go)** (lines 275-294) performs this traversal, recording every object containing an `@id` key in the `rawCfgIndex` map.

This index stores mappings of `identifier → expanded JSON-path`. During indexing, Caddy enforces uniqueness constraints—duplicate `@id` values within the same configuration trigger an error. Both string and numeric values are accepted for `@id`, though numbers are internally stringified when stored in the index.

### API Resolution

When admin API requests arrive at the `/id/<identifier>` endpoint, the `handleConfigID` function in **[`admin.go`](https://github.com/caddyserver/caddy/blob/main/admin.go)** (lines 1093-1126) processes them. This handler looks up the identifier in `rawCfgIndex` and internally rewrites the request to the corresponding `/config/...` path, supporting GET, POST, and PATCH operations.

The literal string `"@id"` is defined as the constant `idKey` in **[`admin.go`](https://github.com/caddyserver/caddy/blob/main/admin.go)** (line 1458), ensuring consistent reference across the codebase.

## Practical Usage of @id Fields

You can embed `@id` fields in both native JSON configurations and Caddyfile syntax.

### JSON Configuration Example

In raw JSON, attach `@id` to any object to assign it a stable reference:

```json
{
  "apps": {
    "http": {
      "@id": "my_http_app",
      "servers": {
        "example": {
          "@id": 100,
          "listen": [":80"],
          "routes": [
            {
              "handle": [
                {
                  "handler": "static_response",
                  "body": "Hello, world!"
                }
              ]
            }
          ]
        }
      }
    }
  }
}

```

### Caddyfile Syntax

The Caddyfile adapter supports `@id` through the `@id <value>` directive:

```caddy
{
  apps {
    http @id my_http_app {
      servers {
        example @id 100 {
          listen :80
          routes {
            respond "Hello, world!"
          }
        }
      }
    }
  }
}

```

### Admin API Access

Once indexed, access objects directly via their identifiers using the `/id/` endpoint:

```bash

# Retrieve the HTTP app configuration using its string ID

curl -s http://localhost:2019/id/my_http_app

# Access the server object using its numeric ID (converted to string "100")

curl -s http://localhost:2019/id/100

```

These requests resolve to their full JSON paths—`/config/apps/http` and `/config/apps/http/servers/example` respectively—while returning the exact object contents.

## Technical Constraints and Requirements

When implementing @id fields in Caddy config, observe these constraints defined in the source code:

- **Uniqueness**: Identifiers must be unique across the entire configuration; duplicates cause indexing failures
- **Type flexibility**: Values may be strings or numbers, but numbers are stringified internally
- **Volatility**: The `rawCfgIndex` exists only in memory; you must persist `@id` fields in your source configuration to maintain identifiers across restarts
- **Scope**: `@id` can be attached to any JSON object, regardless of nesting depth or module type

## Summary

- **@id fields** in Caddy config serve as meta-fields that provide stable identifiers without affecting runtime configuration structure
- The **loading pipeline** strips `@id` via `RemoveMetaFields` in [`admin.go`](https://github.com/caddyserver/caddy/blob/main/admin.go) before unmarshaling, then builds an index via `indexConfigObjects` in [`caddy.go`](https://github.com/caddyserver/caddy/blob/main/caddy.go)
- The **admin API** exposes a shorthand `/id/<identifier>` endpoint handled by `handleConfigID` in [`admin.go`](https://github.com/caddyserver/caddy/blob/main/admin.go) that resolves to full JSON paths
- Values can be **strings or numbers**, must be **unique**, and are stored in the **in-memory** `rawCfgIndex` map
- Both **JSON** and **Caddyfile** formats support `@id` syntax for flexible configuration management

## Frequently Asked Questions

### Can I use @id in the Caddyfile, or is it limited to JSON?

You can use @id in both formats. In JSON, include it as a standard object key. In the Caddyfile, use the `@id <value>` directive immediately after the block opening. The Caddyfile adapter converts these directives to JSON meta-fields during the adaptation phase, resulting in identical runtime behavior.

### What happens if I accidentally use duplicate @id values?

Caddy enforces uniqueness during the indexing phase in `indexConfigObjects`. If duplicate identifiers are detected, Caddy returns an error and refuses to load the configuration. This prevents ambiguous API resolution where multiple objects might claim the same `/id/<identifier>` endpoint.

### Do @id fields persist after Caddy restarts?

No. The `rawCfgIndex` exists only in memory while Caddy runs. To maintain stable identifiers across restarts, you must preserve the `@id` fields in your persistent configuration source (JSON file, Caddyfile, or config database). When Caddy reloads, it rebuilds the index from scratch based on the current configuration.

### Can @id values contain special characters or spaces?

While the source code stringifies numeric values and stores identifiers as map keys, you should generally use URL-safe characters since these values become path segments in the admin API (`/id/<identifier>`). Avoid spaces and special URL-encoded characters to ensure predictable API behavior.