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

# CMS protection

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>Content Management Systems (CMS) typically send information to your website, and this activity can appear malicious due to its automated nature. </p>

    <p>Gcore's Web Application and API Protection (WAAP) product can distinguish between traffic from your CMS administrators and potentially harmful requests. This ensures that administrative activities remain unblocked and your application stays protected. </p>

    <p>The CMS protection policy group contains specific policies that detect when a user is logged into a supported CMS, and automatically adds the user's session to the allowlist. We also maintain a library of known malicious attacks, which allows us to block exploits that have attacked users in the past.</p>

    <Info>
      **Info**

      This policy group is available in the *Pro* and *Enterprise* plans. More details on the [Security pricing page](https://gcore.com/pricing/security#waap).
    </Info>

    ## Allow admin access to your domain

    <p>In some cases, administrative sections of a CMS-based website may be blocked. For example, for WordPress, WAAP may label a change made to the `/wp-admin` section of a CMS-based site as malicious behavior such as cross-site scripting or SQL injection. </p>

    <p>As a result, WAAP will block admins from making any page edits. You can prevent this issue in two ways: [enable the needed policies](/waap/waap-policies/cms-protection#configure-policy-group) in the CMS protection policy group or [allowlist your static IP address](/waap/waap-policies/cms-protection#allowlist-a-static-ip-address). </p>

    ## Configure CMS Protection rules

    <p>You can review the CMS Protection rules and enable or disable them in the [Gcore Customer Portal](https://portal.gcore.com/accounts/reports/dashboard): </p>

    <p>1. Navigate to **WAAP** > **Default Rules**. </p>

    <p>2. In the domain dropdown at the top of the page, select the needed domain.</p>

    <p>3. Click the **CMS Protection** tab to view and adjust the rules.</p>

    <Info>
      **Info**

      Most CMS protection policies allow authenticated admin sessions. Only the WordPress WAF ruleset policy actively blocks requests.
    </Info>

    <p>If you don't see your CMS, you can allow admin access by adding your IP address to the allowlist. Contact our [Support team](mailto:support@gcore.com) for assistance.</p>

    | Policy                             | Description                                              |
    | ---------------------------------- | -------------------------------------------------------- |
    | WordPress WAF ruleset              | Block requests that are potentially a WordPress exploit. |
    | Logged-in WordPress admins         | Allow requests from logged-in WordPress admins.          |
    | Logged-in MODX admins              | Allow requests from logged-in MODX admins.               |
    | Logged-in Drupal admins            | Allow requests from logged-in Drupal admins.             |
    | Logged-in Joomla admins            | Allow requests from logged-in Joomla admins.             |
    | Logged-in allowlist Magento admins | Allow requests from logged-in Magento admins.            |
    | Logged-in Umbraco admins           | Allow requests from logged-in Umbraco admins.            |
    | Logged-in PimCore admins           | Allow requests from logged-in PimCore admins.            |

    <p>If you enable a particular policy for your CMS, the admin CMS session will be allowlisted when that admin user logs into the site. </p>

    <Info>
      **Tip**

      We recommend disabling policies for Content Management Systems that you don't use.
    </Info>

    ### Allowlist a static IP address

    <p>If you don't see your CMS in the list of policies under the CMS policy group, you can allow admin access to your site as follows: </p>

    <p>1. In the Gcore Customer Portal, navigate to **WAAP** > **Firewall**. </p>

    <p>2. In the domain dropdown at the top of the page, select the needed domain.</p>

    <p>3. In the **Allowed IPs** tab, click **Add IP/IP range**. </p>

    <Frame>
      <img src="https://mintcdn.com/gcore/bDhdzFkD6lztVu55/images/docs/waap/waap-policies/cms-protection/firewall-page.png?fit=max&auto=format&n=bDhdzFkD6lztVu55&q=85&s=57fee329e94b657f2c5814ad010dd1e5" alt="Firewall page with the Allowed IPs tab" width="1300" height="195" data-path="images/docs/waap/waap-policies/cms-protection/firewall-page.png" />
    </Frame>

    <p>4. Enter your public IP address so all traffic from your IP will be allowed and won't be blocked by the WAAP for any type of request.</p>

    <p>5. (Optional) Add a description.</p>

    <p>6. Click **Save** to apply the changes.</p>
  </MethodSection>

  <MethodSection id="api" label="REST API">
    <p>The [Policies](/api-reference/waap#policies) endpoints manage CMS Protection rules ? preventing WAAP from blocking legitimate CMS admin activity. A firewall allowlist rule can also be created for an admin's static IP address. Response examples include only the fields used in each step.</p>

    <Info>
      An [API token](/account-settings/api-tokens) is required, along with the ID of a [WAAP-protected domain](/waap/getting-started/configure-waap-for-a-domain) and the [Python](/developer-tools/sdks/python) or [Go](/developer-tools/sdks/go) SDK installed for SDK examples. CMS Protection policies require a Pro or Enterprise WAAP plan. IP allowlist rules are available on all plans.
    </Info>

    ```bash theme={null}
    export GCORE_API_KEY="{YOUR_API_KEY}"
    export WAAP_DOMAIN_ID="{YOUR_DOMAIN_ID}"
    ```

    ## View policy states

    <p>Retrieve the current mode of all CMS Protection policies for a domain.</p>

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

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

        rule_sets = client.waap.domains.list_rule_sets(domain_id)
        cms = next(
            rs for rs in rule_sets if rs.resource_slug == "cms-protection"
        )

        for policy in cms.rules:
            status = "enabled" if policy.mode else "disabled"
            print(f"{policy.name}: {status} ({policy.id})")
        ```
      </Tab>

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

        import (
            "context"
            "fmt"
            "os"
            "strconv"

            gcore "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")))
            domainID, _ := strconv.ParseInt(os.Getenv("WAAP_DOMAIN_ID"), 10, 64)

            ruleSets, _ := client.Waap.Domains.ListRuleSets(context.Background(), domainID)
            for _, rs := range *ruleSets {
                if rs.ResourceSlug == "cms-protection" {
                    for _, policy := range rs.Rules {
                        status := "disabled"
                        if policy.Mode {
                            status = "enabled"
                        }
                        fmt.Printf("%s: %s (%s)\n", policy.Name, status, policy.ID)
                    }
                }
            }
        }
        ```
      </Tab>

      <Tab title="curl">
        ```bash theme={null}
        curl -X GET "https://api.gcore.com/waap/v1/domains/${WAAP_DOMAIN_ID}/rule-sets" \
          -H "Authorization: APIKey ${GCORE_API_KEY}" \
          | jq '.[] | select(.resource_slug == "cms-protection") | .rules[] | {name, id, mode}'
        ```
      </Tab>
    </Tabs>

    <p>The response includes each policy in the group with its ID and current state. `mode: true` means enabled; `mode: false` means disabled.</p>

    ## Toggle a policy

    <p>Switch a policy between enabled and disabled. Each call flips the current mode. Use the policy ID returned by the [View policy states](#view-policy-states) request.</p>

    <Warning>
      This endpoint toggles the current state rather than setting a specific value. If the target state is unknown, check the current policy state first using the View policy states request.
    </Warning>

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

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

        # Find the policy ID by name
        rule_sets = client.waap.domains.list_rule_sets(domain_id)
        cms = next(
            rs for rs in rule_sets if rs.resource_slug == "cms-protection"
        )
        policy = next(r for r in cms.rules if r.name == "Logged-in WordPress admins")

        result = client.waap.domains.policies.toggle(policy.id, domain_id=domain_id)
        status = "enabled" if result.mode else "disabled"
        print(f"Logged-in WordPress admins is now {status}")
        ```
      </Tab>

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

        import (
            "context"
            "fmt"
            "os"
            "strconv"

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

        func main() {
            client := gcore.NewClient(option.WithAPIKey(os.Getenv("GCORE_API_KEY")))
            domainID, _ := strconv.ParseInt(os.Getenv("WAAP_DOMAIN_ID"), 10, 64)

            // Find the policy ID by name
            ruleSets, _ := client.Waap.Domains.ListRuleSets(context.Background(), domainID)
            var policyID string
            for _, rs := range *ruleSets {
                if rs.ResourceSlug == "cms-protection" {
                    for _, p := range rs.Rules {
                        if p.Name == "Logged-in WordPress admins" {
                            policyID = p.ID
                        }
                    }
                }
            }

            result, _ := client.Waap.Domains.Policies.Toggle(context.Background(), policyID, waap.DomainPolicyToggleParams{DomainID: domainID})
            status := "disabled"
            if result.Mode {
                status = "enabled"
            }
            fmt.Printf("Logged-in WordPress admins is now %s\n", status)
        }
        ```
      </Tab>

      <Tab title="curl">
        ```bash theme={null}
        # Set POLICY_ID to the policy ID from the Policy reference or View policy states response
        export POLICY_ID="{POLICY_ID}"

        curl -X PATCH "https://api.gcore.com/waap/v1/domains/${WAAP_DOMAIN_ID}/policies/${POLICY_ID}/toggle" \
          -H "Authorization: APIKey ${GCORE_API_KEY}"
        ```

        Response:

        ```json theme={null}
        {"mode": true}
        ```
      </Tab>
    </Tabs>

    <p>The API returns the updated policy object. `mode: true` confirms the policy is now enabled; `mode: false` confirms disabled.</p>

    ## Allowlist an IP address

    <p>Create a firewall rule that allows all traffic from a specific IP address, bypassing WAF inspection. Use this approach when the CMS admin's IP address is static and their CMS is not in the policy list above.</p>

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

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

        rule = client.waap.domains.firewall_rules.create(
            domain_id,
            name="CMS admin IP allowlist",
            enabled=True,
            action={"allow": {}},
            conditions=[{"ip": {"ip_address": "203.0.113.10"}}],
        )
        print(f"Created firewall rule ID: {rule.id}")
        ```
      </Tab>

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

        import (
            "context"
            "fmt"
            "os"
            "strconv"

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

        func main() {
            client := gcore.NewClient(option.WithAPIKey(os.Getenv("GCORE_API_KEY")))
            domainID, _ := strconv.ParseInt(os.Getenv("WAAP_DOMAIN_ID"), 10, 64)

            rule, err := client.Waap.Domains.FirewallRules.New(
                context.Background(),
                domainID,
                waap.DomainFirewallRuleNewParams{
                    Name:    "CMS admin IP allowlist",
                    Enabled: true,
                    Action: waap.DomainFirewallRuleNewParamsAction{
                        Allow: map[string]any{},
                    },
                    Conditions: []waap.DomainFirewallRuleNewParamsCondition{
                        {
                            IP: waap.DomainFirewallRuleNewParamsConditionIP{
                                IPAddress: "203.0.113.10",
                            },
                        },
                    },
                },
            )
            if err != nil {
                fmt.Printf("Error: %v\n", err)
                return
            }
            fmt.Printf("Created firewall rule ID: %d\n", rule.ID)
        }
        ```
      </Tab>

      <Tab title="curl">
        ```bash theme={null}
        curl -X POST "https://api.gcore.com/waap/v1/domains/${WAAP_DOMAIN_ID}/firewall-rules" \
          -H "Authorization: APIKey ${GCORE_API_KEY}" \
          -H "Content-Type: application/json" \
          -d '{
            "name": "CMS admin IP allowlist",
            "enabled": true,
            "action": {"allow": {}},
            "conditions": [{"ip": {"ip_address": "203.0.113.10"}}]
          }'
        ```

        Response:

        ```json theme={null}
        {
          "id": 268353,
          "name": "CMS admin IP allowlist",
          "enabled": true,
          "action": {"allow": {}},
          "conditions": [{"ip": {"negation": false, "ip_address": "203.0.113.10"}}]
          // ...
        }
        ```
      </Tab>
    </Tabs>

    <p>The API returns the created firewall rule object with its assigned <code>id</code>.</p>

    ## Policy reference

    <Accordion title="All CMS Protection policies">
      **Action** indicates what happens when the policy triggers: **Block** rejects the request; **Allow** permits the authenticated admin session, bypassing further WAF inspection.

      | Policy                             | Purpose                                                      | Action | Default  |
      | ---------------------------------- | ------------------------------------------------------------ | ------ | -------- |
      | WordPress WAF ruleset              | Blocks requests that match known WordPress exploit patterns. | Block  | Enabled  |
      | Logged-in WordPress admins         | Allows requests from logged-in WordPress admin sessions.     | Allow  | Disabled |
      | Logged-in MODX admins              | Allows requests from logged-in MODX admin sessions.          | Allow  | Disabled |
      | Logged-in Drupal admins            | Allows requests from logged-in Drupal admin sessions.        | Allow  | Disabled |
      | Logged-in Joomla admins            | Allows requests from logged-in Joomla admin sessions.        | Allow  | Disabled |
      | Logged-in allowlist Magento admins | Allows requests from logged-in Magento admin sessions.       | Allow  | Disabled |
      | Logged-in Umbraco admins           | Allows requests from logged-in Umbraco admin sessions.       | Allow  | Disabled |
      | Logged-in PimCore admins           | Allows requests from logged-in PimCore admin sessions.       | Allow  | Disabled |
    </Accordion>
  </MethodSection>
</MethodSwitch>
