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:
On error:
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
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 publicnginx_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
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.jsonwith all entries equal to:ipremoved 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.
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
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
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.
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
| 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.
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
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, oryum install -y nginx-module-njsdepending on which package manager is present. - Prepends
load_module modules/ngx_http_js_module.so;to/etc/nginx/nginx.confif not already present. - Runs
nginx -t, thensystemctl reload nginxon 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.
Example
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
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.jsonor the publicnginx_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
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.jsonor the publicnginx_acl_public.jsonwith 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.
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.jsonor the publicnginx_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) tonginx_acl_public.jsonplus 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
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.jsonor the publicnginx_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
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.jsonor the publicnginx_acl_public.jsonwith 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.
| Field | Type | Required | Description |
|---|---|---|---|
website_id |
integer | no | Website, used only if the query parameter is missing. |
Response - 200 OK
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 runssystemctl 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
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.jsonor the publicnginx_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
| 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
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_ordervalues) in the owning user'snginx_acl.jsonor the publicnginx_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
| 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
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