Skip to content

Nginx ACL

F5-style ACL policies compiled to njs. Rulesets are policies with one action; rules are combined with any (OR) or all (AND); conditions inside a rule are AND. Reusable IP groups with CIDR support.

All endpoints below are mounted under /api. Authentication: Authorization: Bearer <token> unless noted as public.

Endpoint count: 21

Endpoints

Method Path Handler Guards
DELETE /api/nginx-acl/conditions/:id deleteACLCondition RequireSuperuser(), RequirePlugin(db, "nginx-acl")
DELETE /api/nginx-acl/ip-entries/:ip deleteACLIPEntry RequireSuperuser(), RequirePlugin(db, "nginx-acl")
GET /api/nginx-acl/ip-groups listACLIPGroups RequireSuperuser(), RequirePlugin(db, "nginx-acl")
POST /api/nginx-acl/ip-groups createACLIPGroup RequireSuperuser(), RequirePlugin(db, "nginx-acl")
DELETE /api/nginx-acl/ip-groups/:name deleteACLIPGroup RequireSuperuser(), RequirePlugin(db, "nginx-acl")
GET /api/nginx-acl/ip-groups/:name/entries listACLIPEntries RequireSuperuser(), RequirePlugin(db, "nginx-acl")
POST /api/nginx-acl/ip-groups/:name/entries addACLIPEntry RequireSuperuser(), RequirePlugin(db, "nginx-acl")
POST /api/nginx-acl/njs/install installNjsModule RequireSuperuser(), RequirePlugin(db, "nginx-acl")
GET /api/nginx-acl/njs/status getNjsStatus RequireSuperuser(), RequirePlugin(db, "nginx-acl")
DELETE /api/nginx-acl/rules/:id deleteACLRule RequireSuperuser(), RequirePlugin(db, "nginx-acl")
PUT /api/nginx-acl/rules/:id updateACLRule RequireSuperuser(), RequirePlugin(db, "nginx-acl")
POST /api/nginx-acl/rules/:id/conditions addACLCondition RequireSuperuser(), RequirePlugin(db, "nginx-acl")
GET /api/nginx-acl/rulesets listACLRulesets RequireSuperuser(), RequirePlugin(db, "nginx-acl")
POST /api/nginx-acl/rulesets createACLRuleset RequireSuperuser(), RequirePlugin(db, "nginx-acl")
DELETE /api/nginx-acl/rulesets/:id deleteACLRuleset RequireSuperuser(), RequirePlugin(db, "nginx-acl")
PUT /api/nginx-acl/rulesets/:id updateACLRuleset RequireSuperuser(), RequirePlugin(db, "nginx-acl")
POST /api/nginx-acl/rulesets/:id/apply applyACLRuleset RequireSuperuser(), RequirePlugin(db, "nginx-acl")
GET /api/nginx-acl/rulesets/:id/rules listACLRules RequireSuperuser(), RequirePlugin(db, "nginx-acl")
POST /api/nginx-acl/rulesets/:id/rules createACLRule RequireSuperuser(), RequirePlugin(db, "nginx-acl")
POST /api/nginx-acl/rulesets/:id/rules/reorder reorderACLRules RequireSuperuser(), RequirePlugin(db, "nginx-acl")
POST /api/nginx-acl/rulesets/reorder reorderACLRulesets RequireSuperuser(), RequirePlugin(db, "nginx-acl")

Source

Handlers for this section live in api/routes/handlers_*.go in the StarkOne source. Each handler binds its request body with gin.ShouldBindJSON(&req) and responds with the standard envelope:

{ "data": { ... } }

On error:

{ "error": { "code": "string", "message": "string" } }

Reference

DELETE /api/nginx-acl/conditions/:id

Deletes a single condition from a rule, identified by its zero-based index within that rule's condition list, searching the website owner's ACL config and then the public library.

Guards: RequireSuperuser(), RequirePlugin(db, "nginx-acl")

Path parameters

Name Type Description
id integer Zero-based index of the condition within the rule's conditions array.

Query parameters

Name Type Optional Description
website_id integer no Website whose owning user's ACL config is searched.
rule_id string no ID of the rule that owns the condition.

Response - 200 OK

{ "data": { "message": "deleted" } }

Errors

Status Code Meaning
400 bad_request website_id or rule_id missing.
404 not_found Website not found, or rule/condition not found in user or public config.
500 read_error Failed to read the ACL JSON config.
500 write_error Failed to write the ACL JSON config.

Side effects

  • Rewrites the owning user's nginx_acl.json (or the public nginx_acl_public.json) with the condition removed. No nginx reload occurs here.

Example

curl -X DELETE "https://panel.example.com:2087/api/nginx-acl/conditions/0?website_id=1&rule_id=r_1700000000000" \
  -H "Authorization: Bearer $TOKEN"

DELETE /api/nginx-acl/ip-entries/:ip

Removes every entry matching the given IP/subnet value from a named IP group in the website owner's ACL config.

Guards: RequireSuperuser(), RequirePlugin(db, "nginx-acl")

Path parameters

Name Type Description
ip string The IP address or CIDR subnet value to remove (matched by exact string value).

Query parameters

Name Type Optional Description
website_id integer no Website whose owning user's ACL config is modified.
group string no Name of the IP group to remove the entry from.

Response - 200 OK

{ "data": { "message": "deleted" } }

Errors

Status Code Meaning
400 bad_request website_id or group missing.
404 not_found Website not found, or the named group does not exist.
500 read_error Failed to read the ACL JSON config.
500 write_error Failed to write the ACL JSON config.

Side effects

  • Rewrites the owning user's nginx_acl.json with all entries equal to :ip removed from the group. No nginx reload here.

Example

curl -X DELETE "https://panel.example.com:2087/api/nginx-acl/ip-entries/203.0.113.5?website_id=1&group=blocklist" \
  -H "Authorization: Bearer $TOKEN"

GET /api/nginx-acl/ip-groups

Lists the IP groups defined in the website owner's ACL config, each with its entry count.

Guards: RequireSuperuser(), RequirePlugin(db, "nginx-acl")

Query parameters

Name Type Optional Description
website_id integer no Website whose owning user's IP groups are listed.

Response - 200 OK

description is always an empty string (descriptions are not persisted). id and name are both the group name.

{ "data": [ { "id": "blocklist", "name": "blocklist", "description": "", "entry_count": 3 } ] }

Errors

Status Code Meaning
400 bad_request website_id missing.
404 not_found Website not found.
500 read_error Failed to read the ACL JSON config.

Example

curl "https://panel.example.com:2087/api/nginx-acl/ip-groups?website_id=1" \
  -H "Authorization: Bearer $TOKEN"

POST /api/nginx-acl/ip-groups

Creates a new, empty IP group in the website owner's ACL config.

Guards: RequireSuperuser(), RequirePlugin(db, "nginx-acl")

Request body

{ "name": "string (required)", "description": "string (optional)", "website_id": "integer (required)" }
Field Type Required Description
name string yes Group name; must be unique within the user's config.
description string no Echoed back in the response but not persisted.
website_id integer yes Website whose owning user's config gets the group.

Response - 201 Created

{ "data": { "id": "blocklist", "name": "blocklist", "description": "Known bad actors" } }

Errors

Status Code Meaning
400 bad_request Body invalid, name empty, website_id missing/zero, or group already exists.
404 not_found Website not found.
500 read_error Failed to read the ACL JSON config.
500 write_error Failed to write the ACL JSON config.

Side effects

  • Adds an empty group to the owning user's nginx_acl.json (creating the user data directory if needed). No nginx reload here.

Example

curl -X POST https://panel.example.com:2087/api/nginx-acl/ip-groups \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "blocklist", "description": "Known bad actors", "website_id": 1}'

DELETE /api/nginx-acl/ip-groups/:name

Deletes a named IP group (and all its entries) from the website owner's ACL config.

Guards: RequireSuperuser(), RequirePlugin(db, "nginx-acl")

Path parameters

Name Type Description
name string Name of the IP group to delete.

Query parameters

Name Type Optional Description
website_id integer no Website whose owning user's config is modified.

Response - 200 OK

{ "data": { "message": "deleted" } }

Errors

Status Code Meaning
400 bad_request website_id missing.
404 not_found Website not found, or the named group does not exist.
500 read_error Failed to read the ACL JSON config.
500 write_error Failed to write the ACL JSON config.

Side effects

  • Removes the group from the owning user's nginx_acl.json. No nginx reload here.

Example

curl -X DELETE "https://panel.example.com:2087/api/nginx-acl/ip-groups/blocklist?website_id=1" \
  -H "Authorization: Bearer $TOKEN"

GET /api/nginx-acl/ip-groups/:name/entries

Lists the IP/subnet entries in a named IP group.

Guards: RequireSuperuser(), RequirePlugin(db, "nginx-acl")

Path parameters

Name Type Description
name string Name of the IP group to list entries for.

Query parameters

Name Type Optional Description
website_id integer no Website whose owning user's config is read.

Response - 200 OK

id is the zero-based array index of the entry within the group.

{ "data": [ { "id": 0, "group_id": "blocklist", "ip_or_subnet": "203.0.113.0/24" } ] }

Errors

Status Code Meaning
400 bad_request website_id missing.
404 not_found Website not found, or the named group does not exist.
500 read_error Failed to read the ACL JSON config.

Example

curl "https://panel.example.com:2087/api/nginx-acl/ip-groups/blocklist/entries?website_id=1" \
  -H "Authorization: Bearer $TOKEN"

POST /api/nginx-acl/ip-groups/:name/entries

Appends an IP address or CIDR subnet to a named IP group.

Guards: RequireSuperuser(), RequirePlugin(db, "nginx-acl")

Path parameters

Name Type Description
name string Name of the IP group to add the entry to.

Request body

{ "ip_or_subnet": "string (required)", "website_id": "integer (required)" }
Field Type Required Description
ip_or_subnet string yes IP or CIDR to add; whitespace is trimmed before storing.
website_id integer yes Website whose owning user's config is modified.

Response - 201 Created

id is the zero-based index of the newly added entry.

{ "data": { "id": 3 } }

Errors

Status Code Meaning
400 bad_request Body invalid, ip_or_subnet empty, or website_id missing/zero.
404 not_found Website not found, or the named group does not exist.
500 read_error Failed to read the ACL JSON config.
500 write_error Failed to write the ACL JSON config.

Side effects

  • Appends the entry to the group in the owning user's nginx_acl.json. No nginx reload here.

Example

curl -X POST https://panel.example.com:2087/api/nginx-acl/ip-groups/blocklist/entries \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"ip_or_subnet": "203.0.113.0/24", "website_id": 1}'

POST /api/nginx-acl/njs/install

Installs the nginx njs (JavaScript) module via the host's package manager, ensures it is loaded in nginx.conf, then tests and reloads nginx.

Guards: RequireSuperuser(), RequirePlugin(db, "nginx-acl")

Response - 200 OK

{ "data": { "message": "njs module installed and loaded" } }

Errors

Status Code Meaning
500 unsupported No supported package manager (apt, dnf, yum) found.
500 install_failed Package install command exited non-zero; message contains command output.
500 nginx_test_failed nginx -t failed after adding the load_module line; message contains test output.

Side effects

  • Runs apt install -y libnginx-mod-http-js, dnf install -y nginx-mod-http-js, or yum install -y nginx-module-njs depending on which package manager is present.
  • Prepends load_module modules/ngx_http_js_module.so; to /etc/nginx/nginx.conf if not already present.
  • Runs nginx -t, then systemctl reload nginx on success.

Example

curl -X POST https://panel.example.com:2087/api/nginx-acl/njs/install \
  -H "Authorization: Bearer $TOKEN"

GET /api/nginx-acl/njs/status

Reports whether the njs module binary is installed on disk and whether it is loaded by the nginx configuration.

Guards: RequireSuperuser(), RequirePlugin(db, "nginx-acl")

Response - 200 OK

installed is true if the module .so exists in any known modules path; loaded is true if ngx_http_js_module appears in nginx.conf or any file under /etc/nginx/modules-enabled.

{ "data": { "installed": true, "loaded": true } }

Example

curl https://panel.example.com:2087/api/nginx-acl/njs/status \
  -H "Authorization: Bearer $TOKEN"

DELETE /api/nginx-acl/rules/:id

Deletes a rule by ID, searching every ruleset in the website owner's config and then the public library.

Guards: RequireSuperuser(), RequirePlugin(db, "nginx-acl")

Path parameters

Name Type Description
id string ID of the rule to delete.

Query parameters

Name Type Optional Description
website_id integer no Website whose owning user's config is searched first.

Response - 200 OK

{ "data": { "message": "deleted" } }

Errors

Status Code Meaning
400 bad_request website_id missing.
404 not_found Website not found, or rule not found in user or public config.
500 read_error Failed to read the ACL JSON config.
500 write_error Failed to write the ACL JSON config.

Side effects

  • Removes the rule from its ruleset in the owning user's nginx_acl.json or the public nginx_acl_public.json. No nginx reload here.

Example

curl -X DELETE "https://panel.example.com:2087/api/nginx-acl/rules/r_1700000000000?website_id=1" \
  -H "Authorization: Bearer $TOKEN"

PUT /api/nginx-acl/rules/:id

Updates fields of a rule (name, action, sort order, enabled flag, and/or its full condition list), searching the website owner's config then the public library. All body fields are optional pointers; only those present are applied.

Guards: RequireSuperuser(), RequirePlugin(db, "nginx-acl")

Path parameters

Name Type Description
id string ID of the rule to update.

Query parameters

Name Type Optional Description
website_id integer yes Website whose owning user's config is searched. Falls back to the body website_id if absent.

Request body

When conditions is present it replaces the rule's entire condition list; each condition's empty condition_operator defaults to contains. The legacy single-condition fields (condition_type, etc.) are accepted by the binder but are not applied by the update path.

{
  "name": "string (optional)",
  "action_type": "string (optional)",
  "action_value": "string (optional)",
  "sort_order": "integer (optional)",
  "enabled": "boolean (optional)",
  "conditions": [
    { "condition_type": "string", "condition_field": "string", "condition_operator": "string", "condition_value": "string" }
  ],
  "website_id": "integer (optional, used if query website_id absent)"
}
Field Type Required Description
name string no New rule name.
action_type string no New rule action type.
action_value string no New rule action value.
sort_order integer no New sort order.
enabled boolean no Enable/disable the rule.
conditions array no Full replacement condition list (see object fields above).
website_id integer no Website, used only if the query parameter is missing.

Response - 200 OK

{ "data": { "message": "updated" } }

Errors

Status Code Meaning
400 bad_request Body invalid, or website_id missing/zero from both query and body.
404 not_found Website not found, or rule not found in user or public config.
500 read_error Failed to read the ACL JSON config.
500 write_error Failed to write the ACL JSON config.

Side effects

  • Rewrites the owning user's nginx_acl.json or the public nginx_acl_public.json with the updated rule. No nginx reload here.

Example

curl -X PUT "https://panel.example.com:2087/api/nginx-acl/rules/r_1700000000000?website_id=1" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "Block bots", "enabled": false}'

POST /api/nginx-acl/rules/:id/conditions

Appends a single condition to an existing rule, searching the website owner's config then the public library.

Guards: RequireSuperuser(), RequirePlugin(db, "nginx-acl")

Path parameters

Name Type Description
id string ID of the rule to add the condition to.

Request body

{
  "condition_type": "string (required)",
  "condition_field": "string (optional)",
  "condition_operator": "string (optional, defaults to \"contains\")",
  "condition_value": "string (optional)",
  "website_id": "integer (required)"
}
Field Type Required Description
condition_type string yes Condition type (e.g. ip, header, path).
condition_field string no Field name the condition applies to (e.g. a header name).
condition_operator string no Comparison operator; defaults to contains when empty.
condition_value string no Value to compare against.
website_id integer yes Website whose owning user's config is searched.

Response - 201 Created

id is a freshly generated synthetic condition ID; it is returned but not stored on the condition itself.

{ "data": { "id": "c_1700000000000" } }

Errors

Status Code Meaning
400 bad_request Body invalid, condition_type empty, or website_id missing/zero.
404 not_found Website not found, or rule not found in user or public config.
500 read_error Failed to read the ACL JSON config.
500 write_error Failed to write the ACL JSON config.

Side effects

  • Appends the condition to the rule in the owning user's nginx_acl.json or the public nginx_acl_public.json. No nginx reload here.

Example

curl -X POST https://panel.example.com:2087/api/nginx-acl/rules/r_1700000000000/conditions \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"condition_type": "header", "condition_field": "User-Agent", "condition_operator": "contains", "condition_value": "curl", "website_id": 1}'

GET /api/nginx-acl/rulesets

Lists the website owner's rulesets plus any public rulesets not already present in the user's config, with rule counts and normalized match mode.

Guards: RequireSuperuser(), RequirePlugin(db, "nginx-acl")

Query parameters

Name Type Optional Description
website_id integer no Website whose owning user's rulesets are listed.

Response - 200 OK

match_mode is normalized to any unless stored as all. assignment_count is always 0.

{
  "data": [
    {
      "id": "rs_1700000000000",
      "name": "Geo block",
      "enabled": true,
      "is_public": false,
      "match_mode": "any",
      "action_type": "block",
      "action_value": "",
      "rule_count": 2,
      "assignment_count": 0
    }
  ]
}

Errors

Status Code Meaning
400 bad_request website_id missing.
404 not_found Website not found.
500 read_error Failed to read the ACL JSON config.

Example

curl "https://panel.example.com:2087/api/nginx-acl/rulesets?website_id=1" \
  -H "Authorization: Bearer $TOKEN"

POST /api/nginx-acl/rulesets

Creates a new ruleset (policy). If is_public is true it is saved to the global public library and also auto-assigned to the creating website's user; otherwise it is saved only in the user's config.

Guards: RequireSuperuser(), RequirePlugin(db, "nginx-acl")

Request body

action_type defaults to block when empty; match_mode is normalized to any unless all.

{
  "name": "string (required)",
  "description": "string (optional)",
  "is_public": "boolean (optional)",
  "match_mode": "string (optional, \"any\" | \"all\")",
  "action_type": "string (optional, defaults to \"block\")",
  "action_value": "string (optional)",
  "website_id": "integer (required)"
}
Field Type Required Description
name string yes Ruleset name.
description string no Echoed back in the response but not persisted.
is_public boolean no If true, store in the public library and auto-assign to the creator's user.
match_mode string no any (OR) or all (AND); normalized to any unless all.
action_type string no Action when matched; defaults to block.
action_value string no Optional action value.
website_id integer yes Website whose owning user owns the ruleset.

Response - 201 Created

{ "data": { "id": "rs_1700000000000", "name": "Geo block", "description": "", "is_public": false } }

Errors

Status Code Meaning
400 bad_request Body invalid, name empty, or website_id missing/zero.
404 not_found Website not found.
500 read_error Failed to read the ACL JSON config.
500 write_error Failed to write the ACL JSON config.

Side effects

  • Writes the new ruleset to the owning user's nginx_acl.json, or (when public) to nginx_acl_public.json plus the creator's user config. No nginx reload here.

Example

curl -X POST https://panel.example.com:2087/api/nginx-acl/rulesets \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "Geo block", "match_mode": "any", "action_type": "block", "website_id": 1}'

DELETE /api/nginx-acl/rulesets/:id

Deletes a ruleset by ID from the website owner's config (falling back to the public library), then regenerates the user's nginx ACL artifacts.

Guards: RequireSuperuser(), RequirePlugin(db, "nginx-acl")

Path parameters

Name Type Description
id string ID of the ruleset to delete.

Query parameters

Name Type Optional Description
website_id integer no Website whose owning user's config is searched first.

Response - 200 OK

{ "data": { "message": "deleted" } }

Errors

Status Code Meaning
400 bad_request website_id missing.
404 not_found Website not found, or ruleset not found in user or public config.
500 read_error Failed to read the ACL JSON config.
500 write_error Failed to write the ACL JSON config.

Side effects

  • Removes the ruleset from the owning user's nginx_acl.json or the public nginx_acl_public.json.
  • Calls generateACLForUser(username) to regenerate the njs/nginx artifacts (cleaning up if the config is now empty); a regeneration failure is logged as a warning and does not fail the request.

Example

curl -X DELETE "https://panel.example.com:2087/api/nginx-acl/rulesets/rs_1700000000000?website_id=1" \
  -H "Authorization: Bearer $TOKEN"

PUT /api/nginx-acl/rulesets/:id

Updates fields of a ruleset (name, public flag, enabled, action, match mode), searching the website owner's config then the public library. All body fields are optional pointers.

Guards: RequireSuperuser(), RequirePlugin(db, "nginx-acl")

Path parameters

Name Type Description
id string ID of the ruleset to update.

Query parameters

Name Type Optional Description
website_id integer yes Website whose owning user's config is searched. Falls back to the body website_id if absent.

Request body

description is accepted by the binder but not applied. match_mode is normalized to any unless all.

{
  "name": "string (optional)",
  "is_public": "boolean (optional)",
  "enabled": "boolean (optional)",
  "match_mode": "string (optional, \"any\" | \"all\")",
  "action_type": "string (optional)",
  "action_value": "string (optional)",
  "website_id": "integer (optional, used if query website_id absent)"
}
Field Type Required Description
name string no New ruleset name.
is_public boolean no New public flag.
enabled boolean no Enable/disable the ruleset.
match_mode string no any or all; normalized to any unless all.
action_type string no New action type.
action_value string no New action value.
website_id integer no Website, used only if the query parameter is missing.

Response - 200 OK

{ "data": { "message": "updated" } }

Errors

Status Code Meaning
400 bad_request Body invalid, or website_id missing/zero from both query and body.
404 not_found Website not found, or ruleset not found in user or public config.
500 read_error Failed to read the ACL JSON config.
500 write_error Failed to write the ACL JSON config.

Side effects

  • Rewrites the owning user's nginx_acl.json or the public nginx_acl_public.json with the updated ruleset. No nginx reload here.

Example

curl -X PUT "https://panel.example.com:2087/api/nginx-acl/rulesets/rs_1700000000000?website_id=1" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"enabled": false, "match_mode": "all"}'

POST /api/nginx-acl/rulesets/:id/apply

Verifies a ruleset exists, regenerates the user's njs ACL, rewrites all of the user's website confs, tests nginx, and reloads it.

Guards: RequireSuperuser(), RequirePlugin(db, "nginx-acl")

Path parameters

Name Type Description
id string ID of the ruleset to apply (existence is verified).

Query parameters

Name Type Optional Description
website_id integer yes Website whose owning user's config is applied. Falls back to the body website_id if absent.

Request body

Optional; used only to supply website_id when it is not given as a query parameter.

{ "website_id": "integer (optional)" }
Field Type Required Description
website_id integer no Website, used only if the query parameter is missing.

Response - 200 OK

{ "data": { "message": "Applied successfully. Nginx reloaded." } }

Errors

Status Code Meaning
400 bad_request website_id missing from both query and body.
400 nginx_error nginx -t failed; message contains the test output.
404 not_found Website not found, or ruleset not found.
500 generate_error njs/ACL generation for the user failed.

Side effects

  • Calls generateACLForUser(username) to write the njs ACL artifacts.
  • For every website owned by the user, runs /usr/sbin/starkone-cli rewrite_user_confs <website_id>.
  • Runs nginx -t; on success runs systemctl reload nginx.

Example

curl -X POST "https://panel.example.com:2087/api/nginx-acl/rulesets/rs_1700000000000/apply?website_id=1" \
  -H "Authorization: Bearer $TOKEN"

GET /api/nginx-acl/rulesets/:id/rules

Lists the rules of a ruleset (found in user or public config), expanding each rule's conditions and also surfacing the first condition's fields at the top level for backward compatibility.

Guards: RequireSuperuser(), RequirePlugin(db, "nginx-acl")

Path parameters

Name Type Description
id string ID of the ruleset whose rules are listed.

Query parameters

Name Type Optional Description
website_id integer no Website whose owning user's config is searched.

Response - 200 OK

Each condition's id is synthetic (<type>_<rule_id>). The top-level condition_* fields mirror the first condition.

{
  "data": [
    {
      "id": "r_1700000000000",
      "ruleset_id": "rs_1700000000000",
      "sort_order": 0,
      "name": "Block curl",
      "enabled": true,
      "action_type": "block",
      "action_value": "",
      "conditions": [
        {
          "id": "header_r_1700000000000",
          "condition_type": "header",
          "condition_field": "User-Agent",
          "condition_operator": "contains",
          "condition_value": "curl"
        }
      ],
      "condition_type": "header",
      "condition_field": "User-Agent",
      "condition_operator": "contains",
      "condition_value": "curl"
    }
  ]
}

Errors

Status Code Meaning
400 bad_request website_id missing.
404 not_found Website not found, or ruleset not found in user or public config.

Example

curl "https://panel.example.com:2087/api/nginx-acl/rulesets/rs_1700000000000/rules?website_id=1" \
  -H "Authorization: Bearer $TOKEN"

POST /api/nginx-acl/rulesets/:id/rules

Creates a rule in a ruleset. Supports both the new multi-condition conditions array and a legacy single-condition shape; at least one condition is required.

Guards: RequireSuperuser(), RequirePlugin(db, "nginx-acl")

Path parameters

Name Type Description
id string ID of the ruleset to add the rule to.

Request body

If conditions is empty but condition_type is set, the legacy single-condition fields are converted into one condition. Each condition's empty operator defaults to contains.

{
  "name": "string (optional)",
  "action_type": "string (required)",
  "action_value": "string (optional)",
  "sort_order": "integer (optional)",
  "conditions": [
    { "condition_type": "string", "condition_field": "string", "condition_operator": "string", "condition_value": "string" }
  ],
  "condition_type": "string (legacy, optional)",
  "condition_field": "string (legacy, optional)",
  "condition_operator": "string (legacy, optional)",
  "condition_value": "string (legacy, optional)",
  "website_id": "integer (required)"
}
Field Type Required Description
name string no Rule name.
action_type string yes Action when the rule matches.
action_value string no Optional action value.
sort_order integer no Sort order within the ruleset.
conditions array no* Multi-condition list (objects as above). Required unless the legacy single-condition fields are provided.
condition_type / condition_field / condition_operator / condition_value string no Legacy single-condition shape; used only when conditions is empty.
website_id integer yes Website whose owning user owns the ruleset.

Response - 201 Created

{ "data": { "id": "r_1700000000000" } }

Errors

Status Code Meaning
400 bad_request Body invalid, action_type empty, website_id missing/zero, or no condition provided.
404 not_found Website not found, or ruleset not found in user or public config.
500 write_error Failed to write the ACL JSON config.

Side effects

  • Appends the rule to the ruleset in the owning user's nginx_acl.json or the public nginx_acl_public.json. No nginx reload here.

Example

curl -X POST https://panel.example.com:2087/api/nginx-acl/rulesets/rs_1700000000000/rules \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "Block curl", "action_type": "block", "conditions": [{"condition_type": "header", "condition_field": "User-Agent", "condition_operator": "contains", "condition_value": "curl"}], "website_id": 1}'

POST /api/nginx-acl/rulesets/:id/rules/reorder

Reorders the rules within a ruleset according to the supplied array of rule IDs; rules are reindexed by sort_order to match the array order. Rules whose IDs are omitted are dropped from the ruleset.

Guards: RequireSuperuser(), RequirePlugin(db, "nginx-acl")

Path parameters

Name Type Description
id string ID of the ruleset whose rules are reordered.

Request body

{ "ids": ["r_1700000000001", "r_1700000000000"], "website_id": "integer (required)" }
Field Type Required Description
ids array of string yes Rule IDs in the desired order; must be non-empty.
website_id integer yes Website whose owning user owns the ruleset.

Response - 200 OK

{ "data": { "message": "reordered" } }

Errors

Status Code Meaning
400 bad_request Body invalid or ids empty.
400 bad_request website_id missing/zero.
404 not_found Website not found, or ruleset not found in user or public config.
500 write_error Failed to write the ACL JSON config.

Side effects

  • Rewrites the ruleset's rule order (and sort_order values) in the owning user's nginx_acl.json or the public nginx_acl_public.json. No nginx reload here.

Example

curl -X POST https://panel.example.com:2087/api/nginx-acl/rulesets/rs_1700000000000/rules/reorder \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"ids": ["r_1700000000001", "r_1700000000000"], "website_id": 1}'

POST /api/nginx-acl/rulesets/reorder

Reorders the website owner's rulesets according to the supplied array of ruleset IDs. Rulesets whose IDs are omitted from the array are dropped from the user's config.

Guards: RequireSuperuser(), RequirePlugin(db, "nginx-acl")

Request body

{ "ids": ["rs_1700000000001", "rs_1700000000000"], "website_id": "integer (required)" }
Field Type Required Description
ids array of string yes Ruleset IDs in the desired order; must be non-empty.
website_id integer yes Website whose owning user's rulesets are reordered; must be non-zero.

Response - 200 OK

{ "data": { "message": "reordered" } }

Errors

Status Code Meaning
400 bad_request Body invalid, ids empty, or website_id zero.
404 not_found Website not found.
500 write_failed Failed to write the ACL JSON config.

Side effects

  • Rewrites the ruleset order in the owning user's nginx_acl.json. Only the user config is touched (not the public library). No nginx reload here.

Example

curl -X POST https://panel.example.com:2087/api/nginx-acl/rulesets/reorder \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"ids": ["rs_1700000000001", "rs_1700000000000"], "website_id": 1}'