> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gcore.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Getting started with DNSSEC

export const MethodSection = ({children}) => children ?? null;

export const MethodSwitch = ({children}) => {
  const tabs = React.Children.toArray(children).map(c => {
    if (!c || !c.props) return null;
    if (c.props.id) return c;
    const inner = c.props.children;
    if (inner && inner.props && inner.props.id) return inner;
    return null;
  }).filter(Boolean);
  const firstId = tabs.length > 0 ? tabs[0].props.id : "";
  const [active, setActive] = React.useState(firstId);
  React.useEffect(() => {
    try {
      const saved = localStorage.getItem("gcore_docs_method");
      if (saved && tabs.find(t => t.props.id === saved)) {
        setActive(saved);
      }
    } catch (_) {}
  }, []);
  React.useEffect(() => {
    try {
      document.querySelectorAll("h2[id], h3[id]").forEach(heading => {
        const visible = heading.offsetParent !== null;
        document.querySelectorAll(`a[href="#${heading.id}"]`).forEach(link => {
          if (link.closest("h1,h2,h3,h4,h5,h6")) return;
          const li = link.closest("li");
          if (li) li.style.display = visible ? "" : "none";
        });
      });
    } catch (_) {}
    window.dispatchEvent(new Event("scroll"));
  }, [active]);
  const handleClick = id => {
    setActive(id);
    try {
      localStorage.setItem("gcore_docs_method", id);
    } catch (_) {}
  };
  return <div>
      <div className="not-prose flex gap-0 border-b border-zinc-200 dark:border-zinc-800 mb-8 mt-2" role="tablist">
        {tabs.map(tab => {
    const isActive = active === tab.props.id;
    return <button key={tab.props.id} role="tab" aria-selected={isActive} onClick={() => handleClick(tab.props.id)} className={["px-4 py-2 text-sm font-medium border-b-2 -mb-px transition-colors cursor-pointer", isActive ? "border-primary text-primary" : "border-transparent text-zinc-500 hover:text-zinc-800 dark:hover:text-zinc-200"].join(" ")}>
              {tab.props.label}
            </button>;
  })}
      </div>

      {tabs.map(tab => <div key={tab.props.id} style={{
    display: active === tab.props.id ? "" : "none"
  }}>
          {tab.props.children}
        </div>)}
    </div>;
};

<MethodSwitch>
  <MethodSection id="portal" label="Customer Portal">
    <p>DNSSEC (DNS Security Extensions) adds a layer of authentication to DNS by validating digital signatures across a chain of trust, from the DNS root to the specific record being requested. It protects against cache poisoning attacks, where an attacker redirects users to a malicious site even when a valid web address is entered.</p>

    <Warning>
      DNSSEC does not encrypt traffic between the end user and the resolver. For confidentiality and integrity at that layer, use DNS over HTTPS (DoH) or DNS over TLS (DoT).
    </Warning>

    ## Enable DNSSEC

    1. In the [Gcore Customer Portal](https://portal.gcore.com), navigate to **DNS** > **All zones** and open the zone, or create one (see [manage a DNS zone](/dns/manage-a-dns-zone)).

    2. Enable [advanced interface mode](/dns/about-gcore-dns#interface-modes-non-advanced-and-advanced) if not already active.

    3. Turn on the **DNSSEC** toggle in the zone header.

    <Frame>
      <img src="https://mintcdn.com/gcore/c2xgVRM2Db9qyfjl/images/docs/dns/getting-started-with-dnssec/enable-dnssec-cp.png?fit=max&auto=format&n=c2xgVRM2Db9qyfjl&q=85&s=5ec924eb296ebfff828aee98a6bdd95a" alt="Zone settings header showing the DNSSEC toggle set to OFF and the Interface mode toggle set to Advanced" width="1633" height="436" data-path="images/docs/dns/getting-started-with-dnssec/enable-dnssec-cp.png" />
    </Frame>

    4. In the confirmation dialog, click **Yes, enable**.

    <Frame>
      <img src="https://mintcdn.com/gcore/c2xgVRM2Db9qyfjl/images/docs/dns/getting-started-with-dnssec/enable-dnssec-confirmation.png?fit=max&auto=format&n=c2xgVRM2Db9qyfjl&q=85&s=7ce8c481b0019b9a93bfda21ed44c17e" alt="Confirmation dialog asking whether to enable DNSSEC with Yes, enable and Cancel buttons" width="813" height="430" data-path="images/docs/dns/getting-started-with-dnssec/enable-dnssec-confirmation.png" />
    </Frame>

    5. The **DNSSEC Configuration** dialog opens and shows the DS record details for the zone — copy the value in the **DS record** field.

    <Frame>
      <img src="https://mintcdn.com/gcore/c2xgVRM2Db9qyfjl/images/docs/dns/getting-started-with-dnssec/ds-value-dnssec.png?fit=max&auto=format&n=c2xgVRM2Db9qyfjl&q=85&s=e0c4913d28206adeaa3c31eeaecfa389" alt="DNSSEC Configuration dialog showing Key tag, Algorithm, Digest type, Digest, DS record, and Public key fields with copy buttons" width="1400" height="900" data-path="images/docs/dns/getting-started-with-dnssec/ds-value-dnssec.png" />
    </Frame>

    6. Paste the DS record value into your domain registrar's control panel.

    <Tip>
      If you are transferring a DNS zone from another provider, disable DNSSEC at the old provider first and wait for the DS record TTL to expire before enabling it here.
    </Tip>

    <p>DS propagation can take up to an hour at most registrars, and up to 24 hours in some cases. The **DNSSEC** toggle remains **OFF** until Gcore confirms the DS record at your registrar — once confirmed, the toggle switches to **ON** and the zone's DNSSEC status changes to `active`.</p>

    <Info>
      To view the DS record again after closing the dialog, click the **DNSSEC** toggle — the confirmation and DNSSEC Configuration dialogs reopen.
    </Info>

    ## DNSSEC status lifecycle

    <p>After enabling DNSSEC, the zone moves through a lifecycle driven by what Gcore observes at your domain registrar — not just by your request. Gcore periodically scans the registrar for the DS record and updates the zone's DNSSEC status accordingly.</p>

    | Status             | Meaning                                                                                                       | Action required                                                |
    | ------------------ | ------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
    | `pending`          | DNSSEC is enabled, signing keys are generated, but Gcore has not yet detected the DS record at the registrar. | Add the DS record Gcore provided to your registrar.            |
    | `active`           | A valid DS record is detected at the registrar. The chain of trust is established.                            | None — DNSSEC is working.                                      |
    | `pending-disabled` | Disable was requested, but a DS record is still published at the registrar.                                   | Remove the DS record at the registrar to complete the process. |
    | `disabled`         | DNSSEC is off and no DS record is present.                                                                    | None.                                                          |

    <p>The transition from `pending` to `active` can take from a few minutes to several hours, depending on your registrar's propagation speed and the DS record's TTL. Once active, the status remains `active` — removing the DS record at the registrar does not automatically change it.</p>

    ## Disable DNSSEC

    <Warning>
      Remove the DS record from your registrar before disabling DNSSEC in Gcore. If DNSSEC is disabled in Gcore while the DS record is still published, resolvers will try to validate signatures that no longer exist, causing DNS resolution failures.
    </Warning>

    1. Remove the DS record from your domain registrar's control panel.

    2. Wait for the DS record TTL to expire so cached records clear.

    3. In the Customer Portal, turn off the **DNSSEC** toggle for the zone.
  </MethodSection>

  <MethodSection id="api" label="REST API">
    <p>The DNS API supports enabling and disabling DNSSEC, retrieving DS records, checking zone status, and verifying DS propagation at the registrar. The [Python](/developer-tools/sdks/python) and [Go](/developer-tools/sdks/go) SDKs cover enable, get DS record, and disable operations.</p>

    <Info>
      An [API token](/account-settings/api-tokens) is required. Set these environment variables before running the examples:
    </Info>

    ```bash theme={null}
    export GCORE_API_KEY="{YOUR_API_KEY}"
    export ZONE_NAME="{YOUR_ZONE_NAME}"
    ```

    ## Enable DNSSEC

    <p>Enabling DNSSEC generates signing keys for the zone and returns the DS (Delegation Signer) record — the `ds` field in the response contains the complete DS record string to add at your registrar.</p>

    <Tabs>
      <Tab title="Python SDK">
        ```python theme={null}
        import os
        import gcore

        client = gcore.Gcore(api_key=os.environ["GCORE_API_KEY"])

        result = client.dns.zones.dnssec.update(
            os.environ["ZONE_NAME"],
            enabled=True,
        )

        print(f"DS record: {result.ds}")
        print(f"Key tag:   {result.key_tag}")
        print(f"Algorithm: {result.algorithm}")
        ```
      </Tab>

      <Tab title="Go SDK">
        ```go theme={null}
        package main

        import (
            "context"
            "fmt"
            "os"

            "github.com/G-Core/gcore-go"
            "github.com/G-Core/gcore-go/dns"
            "github.com/G-Core/gcore-go/option"
        )

        func main() {
            client := gcore.NewClient(option.WithAPIKey(os.Getenv("GCORE_API_KEY")))

            result, err := client.DNS.Zones.Dnssec.Update(
                context.Background(),
                os.Getenv("ZONE_NAME"),
                dns.ZoneDnssecUpdateParams{Enabled: gcore.Bool(true)},
            )
            if err != nil {
                fmt.Fprintf(os.Stderr, "enable DNSSEC: %v\n", err)
                os.Exit(1)
            }

            fmt.Printf("DS record: %s\n", result.Ds)
            fmt.Printf("Key tag:   %d\n", result.KeyTag)
            fmt.Printf("Algorithm: %s\n", result.Algorithm)
        }
        ```
      </Tab>

      <Tab title="curl">
        ```bash theme={null}
        curl -X PATCH "https://api.gcore.com/dns/v2/zones/${ZONE_NAME}/dnssec" \
          -H "Authorization: APIKey ${GCORE_API_KEY}" \
          -H "Content-Type: application/json" \
          -d '{"enabled": true}'
        ```
      </Tab>
    </Tabs>

    <p>Copy the `ds` field value from the response and add it to your registrar's control panel. The zone enters the `pending` state until Gcore detects the DS record at your registrar, which can take from a few minutes to several hours.</p>

    ## Get DS record

    <p>To retrieve the DS record for a zone that already has DNSSEC enabled:</p>

    <Tabs>
      <Tab title="Python SDK">
        ```python theme={null}
        import os
        import gcore

        client = gcore.Gcore(api_key=os.environ["GCORE_API_KEY"])

        ds_info = client.dns.zones.dnssec.get(os.environ["ZONE_NAME"])

        print(f"DS record: {ds_info.ds}")
        print(f"Key tag:   {ds_info.key_tag}")
        print(f"Algorithm: {ds_info.algorithm}")
        ```
      </Tab>

      <Tab title="Go SDK">
        ```go theme={null}
        package main

        import (
            "context"
            "fmt"
            "os"

            "github.com/G-Core/gcore-go"
            "github.com/G-Core/gcore-go/option"
        )

        func main() {
            client := gcore.NewClient(option.WithAPIKey(os.Getenv("GCORE_API_KEY")))

            dsInfo, err := client.DNS.Zones.Dnssec.Get(
                context.Background(),
                os.Getenv("ZONE_NAME"),
            )
            if err != nil {
                fmt.Fprintf(os.Stderr, "get DS record: %v\n", err)
                os.Exit(1)
            }

            fmt.Printf("DS record: %s\n", dsInfo.Ds)
            fmt.Printf("Key tag:   %d\n", dsInfo.KeyTag)
            fmt.Printf("Algorithm: %s\n", dsInfo.Algorithm)
        }
        ```
      </Tab>

      <Tab title="curl">
        ```bash theme={null}
        curl "https://api.gcore.com/dns/v2/zones/${ZONE_NAME}/dnssec" \
          -H "Authorization: APIKey ${GCORE_API_KEY}"
        ```
      </Tab>
    </Tabs>

    ## DNSSEC status lifecycle

    <p>The zone response includes two fields that reflect the current DNSSEC state, updated by Gcore's periodic scan of your registrar:</p>

    * `dnssec_status` — the current lifecycle state: `pending`, `active`, `pending-disabled`, or `disabled`
    * `dnssec_status_modified_on` — RFC 3339 timestamp of the last status change

    <p>Both fields are absent from the response if DNSSEC has never been enabled on the zone. A missing field is not an error — it means the zone has no DNSSEC history.</p>

    | Status             | Meaning                                                                     |
    | ------------------ | --------------------------------------------------------------------------- |
    | `pending`          | DNSSEC enabled, keys generated, DS record not yet detected at the registrar |
    | `active`           | DS record detected at the registrar — chain of trust established            |
    | `pending-disabled` | Disable requested, DS record still present at the registrar                 |
    | `disabled`         | DNSSEC off, no DS record present                                            |

    <p>The transition from `pending` to `active` can take from a few minutes to several hours, depending on your registrar's propagation speed and the DS record's TTL. Once active, the status remains `active` — removing the DS record at the registrar does not automatically change it. To turn DNSSEC off, use the disable endpoint.</p>

    <p>To check the current status, fetch the zone and inspect the `dnssec_status` field:</p>

    ```bash theme={null}
    curl "https://api.gcore.com/dns/v2/zones/${ZONE_NAME}" \
      -H "Authorization: APIKey ${GCORE_API_KEY}"
    ```

    <p>Example responses for each state:</p>

    <p>`pending` — keys generated, DS record not yet at registrar:</p>

    ```json theme={null}
    {
      "name": "example.com",
      "enabled": true,
      "status": "active",
      "dnssec_enabled": true,
      "dnssec_status": "pending",
      "dnssec_status_modified_on": "2026-06-25T09:14:02Z"
    }
    ```

    <p>`active` — DS record detected, chain of trust established:</p>

    ```json theme={null}
    {
      "name": "example.com",
      "enabled": true,
      "status": "active",
      "dnssec_enabled": true,
      "dnssec_status": "active",
      "dnssec_status_modified_on": "2026-06-25T11:48:37Z"
    }
    ```

    <p>`pending-disabled` — disable requested, DS record still at registrar:</p>

    ```json theme={null}
    {
      "name": "example.com",
      "enabled": true,
      "status": "active",
      "dnssec_enabled": false,
      "dnssec_status": "pending-disabled",
      "dnssec_status_modified_on": "2026-06-26T08:02:15Z"
    }
    ```

    <p>`disabled` — DNSSEC off, no DS record present:</p>

    ```json theme={null}
    {
      "name": "example.com",
      "enabled": true,
      "status": "active",
      "dnssec_enabled": false,
      "dnssec_status": "disabled",
      "dnssec_status_modified_on": "2026-06-27T10:30:00Z"
    }
    ```

    <p>`dnssec_status` is distinct from the top-level `status` field — `status` reflects zone delegation to Gcore nameservers, while `dnssec_status` reflects the DNSSEC lifecycle.</p>

    ### Check DS record at registrar

    <p>To verify whether a valid DS record is currently detected at the registrar, use the `parent-ds` endpoint:</p>

    ```bash theme={null}
    curl "https://api.gcore.com/dns/v2/zones/${ZONE_NAME}/dnssec/parent-ds" \
      -H "Authorization: APIKey ${GCORE_API_KEY}"
    ```

    <p>The response shows whether the DS record is detected and which parent zone was checked:</p>

    ```json theme={null}
    {
      "has_valid_parent_ds": true,
      "parent_zone": "com."
    }
    ```

    ## Disable DNSSEC

    <p>Send an update with `enabled: false` to disable DNSSEC. This is safe to call once the DS record is removed from the registrar and its TTL has expired.</p>

    <Tabs>
      <Tab title="Python SDK">
        ```python theme={null}
        import os
        import gcore

        client = gcore.Gcore(api_key=os.environ["GCORE_API_KEY"])

        client.dns.zones.dnssec.update(
            os.environ["ZONE_NAME"],
            enabled=False,
        )
        print("DNSSEC disabled.")
        ```
      </Tab>

      <Tab title="Go SDK">
        ```go theme={null}
        package main

        import (
            "context"
            "fmt"
            "os"

            "github.com/G-Core/gcore-go"
            "github.com/G-Core/gcore-go/dns"
            "github.com/G-Core/gcore-go/option"
        )

        func main() {
            client := gcore.NewClient(option.WithAPIKey(os.Getenv("GCORE_API_KEY")))

            _, err := client.DNS.Zones.Dnssec.Update(
                context.Background(),
                os.Getenv("ZONE_NAME"),
                dns.ZoneDnssecUpdateParams{Enabled: gcore.Bool(false)},
            )
            if err != nil {
                fmt.Fprintf(os.Stderr, "disable DNSSEC: %v\n", err)
                os.Exit(1)
            }

            fmt.Println("DNSSEC disabled.")
        }
        ```
      </Tab>

      <Tab title="curl">
        ```bash theme={null}
        curl -X PATCH "https://api.gcore.com/dns/v2/zones/${ZONE_NAME}/dnssec" \
          -H "Authorization: APIKey ${GCORE_API_KEY}" \
          -H "Content-Type: application/json" \
          -d '{"enabled": false}'
        ```
      </Tab>
    </Tabs>

    <p>If a DS record is still published at the registrar when the request is sent, the API returns `409 Conflict`. This protects against breaking DNS validation — resolvers reject responses if the DS record points to keys that no longer exist.</p>

    <p>To proceed despite the active DS record, pass `force_disable: true` in the request body. The zone moves to `pending-disabled` immediately and transitions to `disabled` once the DS record is removed at the registrar:</p>

    ```bash theme={null}
    curl -X PATCH "https://api.gcore.com/dns/v2/zones/${ZONE_NAME}/dnssec" \
      -H "Authorization: APIKey ${GCORE_API_KEY}" \
      -H "Content-Type: application/json" \
      -d '{"enabled": false, "force_disable": true}'
    ```

    <p>After disabling, wait for the DS record TTL to expire before considering the transition complete — cached records at upstream resolvers remain valid until the TTL passes.</p>
  </MethodSection>
</MethodSwitch>
