
Aliyun Esa Manage
- 50 installs
- 396 repo stars
- Updated July 18, 2026
- cinience/alicloud-skills
Provides aliyun-esa-manage capabilities for Claude Code workflows.
About
aliyun-esa-manage enables Provides aliyun-esa-manage capabilities for Claude Code workflows.. Use it to automate and enhance your development workflow with AI-powered capabilities.
- Enhances Claude Code
- Production-ready
Aliyun Esa Manage by the numbers
- 50 all-time installs (skills.sh)
- Ranked #1,305 of 2,245 Frontend Development skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/cinience/alicloud-skills --skill aliyun-esa-manageAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 50 |
|---|---|
| repo stars | ★ 396 |
| Last updated | July 18, 2026 |
| Repository | cinience/alicloud-skills ↗ |
What it does
Provides aliyun-esa-manage capabilities for Claude Code workflows.
Files
Category: service
Edge Security Acceleration (ESA) - Pages, Edge Routine, KV, Site Management, Analytics & More
Use Alibaba Cloud OpenAPI (RPC) with official Python SDK to manage all ESA capabilities.
Alibaba Cloud ESA provides five core capabilities:
- Pages — Deploy HTML or static directories to edge nodes (quick deployment flow based on Edge Routine)
- Edge Routine (ER) — Full lifecycle management of serverless edge functions
- Edge KV — Distributed edge key-value storage with Namespace/Key/Value management
- Site Management — Site management, DNS records, cache rules, certificates, etc.
- Analytics — Traffic analysis, time-series trends, Top-N rankings, bandwidth statistics, request metrics
Use Python SDK uniformly to call ESA OpenAPI.
Prerequisites
- Prepare AccessKey (RAM user/role with least privilege).
- Install Python SDK:
pip install alibabacloud_esa20240910 alibabacloud_tea_openapi alibabacloud_credentials - ESA OpenAPI is RPC style; prefer SDK or OpenAPI Explorer to avoid manual signing.
SDK quickstart
from alibabacloud_esa20240910.client import Client as Esa20240910Client
from alibabacloud_esa20240910 import models as esa_models
from alibabacloud_tea_openapi import models as open_api_models
def create_client(region_id: str = "cn-hangzhou") -> Esa20240910Client:
config = open_api_models.Config(
region_id=region_id,
endpoint="esa.cn-hangzhou.aliyuncs.com",
)
return Esa20240910Client(config)Pages — Edge Page Deployment
Pages is a quick deployment flow based on Edge Routine, deploying HTML or static directories to the edge.
HTML Page Deployment Flow
CreateRoutine → GetRoutineStagingCodeUploadInfo → Upload code to OSS
→ CommitRoutineStagingCode → PublishRoutineCodeVersion(staging)
→ PublishRoutineCodeVersion(production) → GetRoutine(get access URL)Static Directory Deployment Flow
CreateRoutine → CreateRoutineWithAssetsCodeVersion → Package zip and upload to OSS
→ Poll GetRoutineCodeVersionInfo(wait for available)
→ CreateRoutineCodeDeployment(staging) → CreateRoutineCodeDeployment(production)
→ GetRoutine(get access URL)Zip Package Structure
The zip package structure depends on EDGE_ROUTINE_TYPE (automatically determined by checkEdgeRoutineType based on whether entry file and assets directory exist):
- JS_ONLY:
routine/index.js(bundled with esbuild or--no-bundleto read source files directly) - ASSETS_ONLY: All static files under
assets/, maintaining original directory structure - JS_AND_ASSETS:
routine/index.js+assets/static resources (most common)
The assets/ path is relative to assets.directory in configuration. Configuration priority: CLI args > esa.jsonc / esa.toml.
Key Notes
- Function name rules: lowercase letters/numbers/hyphens, start with lowercase letter, length >= 2
- Same name function: Reuse if exists, deploy new version code
- Deploy to both staging and production by default
- After successful deployment, get
defaultRelatedRecordviaGetRoutineas access domain
Bind Custom Domain (CNAME-Access Sites)
For CNAME-access sites, binding a custom domain to a Pages/ER routine requires:
1. CreateRecord(A/AAAA, proxied=true) → Register domain in ESA CDN
2. CreateRoutineRoute(rule expression) → Route traffic to Edge Routine
3. External DNS: CNAME → record_cname → Point domain to ESA CDN
4. ApplyCertificate(lets_encrypt) → Provision SSL certificateCritical: Do NOT use CreateRoutineRelatedRecord for CNAME-access sites — it doesn't create a visible DNS record, causing CDN to fall back to origin. Use A/AAAA record + Route instead.
SDK note: CreateRoutineRoute parameter is rule (ESA rule expression like (http.host eq "domain")), NOT route.
Detailed reference: references/pages.md (section: Bind Custom Domain)
Edge Routine (ER) — Edge Functions
Manage the complete lifecycle of serverless edge functions via Python SDK.
Core Workflow
CreateRoutine → GetRoutineStagingCodeUploadInfo → Upload code to OSS
→ CommitRoutineStagingCode → PublishRoutineCodeVersion
→ (CreateRoutineRoute) → GetRoutineAPI Summary
- Function Management:
CreateRoutine,DeleteRoutine,GetRoutine,GetRoutineUserInfo,ListUserRoutines - Code Version:
GetRoutineStagingCodeUploadInfo,CommitRoutineStagingCode,PublishRoutineCodeVersion,DeleteRoutineCodeVersion - Routes:
CreateRoutineRoute,UpdateRoutineRoute,DeleteRoutineRoute,GetRoutineRoute,ListRoutineRoutes,ListSiteRoutes - Related Records:
CreateRoutineRelatedRecord,DeleteRoutineRelatedRecord,ListRoutineRelatedRecords
ER Code Format
export default {
async fetch(request) {
return new Response("Hello", {
headers: { "content-type": "text/html;charset=UTF-8" },
});
},
};Detailed reference: references/er.md
Edge KV — Edge Key-Value Storage
Distributed edge key-value storage, readable and writable in Edge Routine, also manageable via OpenAPI/SDK.
Core Concepts
- Namespace: Isolation container for KV data, Key max 512 chars, Value max 2MB (high capacity 25MB)
- Supports TTL expiration:
Expiration(Unix timestamp) orExpirationTtl(seconds)
API Summary
- Namespace:
CreateKvNamespace,DeleteKvNamespace,GetKvNamespace,GetKvAccount,DescribeKvAccountStatus - Single Key Operations:
PutKv,GetKv,GetKvDetail,DeleteKv,PutKvWithHighCapacity - Batch Operations:
BatchPutKv,BatchDeleteKv,BatchPutKvWithHighCapacity,BatchDeleteKvWithHighCapacity,ListKvs
Quick Start
client = create_client()
# Create namespace
client.create_kv_namespace(esa_models.CreateKvNamespaceRequest(namespace="my-ns"))
# Write
client.put_kv(esa_models.PutKvRequest(namespace="my-ns", key="k1", value="v1"))
# Read
resp = client.get_kv(esa_models.GetKvRequest(namespace="my-ns", key="k1"))Detailed reference: references/kv.md
Site Management — Site Management
Use Python SDK to manage ESA sites, DNS records, cache rules, etc.
API behavior notes
- Most list APIs support pagination via
PageNumber+PageSize. ListSitesreturns sites across all regions; no need to iterate regions.- Newly created sites start as
pending; complete access verification viaVerifySiteto activate. - Deleting a site removes all associated configuration.
UpdateSiteAccessTypecan switch between CNAME and NS, but switching to CNAME may fail if incompatible DNS records exist.- DNS record APIs (
CreateRecord,ListRecords, etc.) work for both NS and CNAME connected sites. CNAME sites are limited toCNAMEandA/AAAAtypes only, and records cannot disable acceleration (proxy must stay enabled). - DNS record
Typeparameter must be exact: useA/AAAA(notA),CNAME,MX,TXT,NS,SRV,CAA. CreateCacheRulesupports two config types:global(site-wide default) andrule(conditional rule with match expression).
Workflow
1) Confirm target site ID, access type (CNAME/NS), and desired action. 2) Find API group and exact operation name in references/api_overview.md. 3) Call API with Python SDK (preferred) or OpenAPI Explorer. 4) Verify results with describe/list APIs. 5) If you need repeatable inventory or summaries, use scripts/ and write outputs under output/aliyun-esa-manage/.
SDK priority
1) Python SDK (preferred) 2) OpenAPI Explorer 3) Other SDKs (only if Python is not feasible)
Python SDK scripts (recommended for inventory)
- List all ESA sites:
scripts/list_sites.py - Summarize sites by plan:
scripts/summary_sites_by_plan.py - Check site status:
scripts/check_site_status.py - List DNS records for a site:
scripts/list_dns_records.py
Analytics — Traffic Analysis
Query and analyze ESA site traffic data using DescribeSiteTimeSeriesData and DescribeSiteTopData APIs.
Core Features
- Time-Series Data: Query traffic trends with configurable time granularity
- Top-N Rankings: Get rankings by country/IP/host/path/status code dimensions
- Multiple Metrics: Traffic, Requests, RequestTraffic, PageView
- Rich Dimensions: Country, province, ISP, browser, device, host, path, status code, etc.
Two Main APIs
1. DescribeSiteTimeSeriesData - Time-Series Trends
Query traffic trends over time, returning aggregated data points.
Time Granularity Rules:
| Time Range | Interval | Interval Value |
|---|---|---|
| <= 3 hours | 1 minute | 60 |
| 3-12 hours | 5 minutes | 300 |
| 12 hours - 1 day | 15 minutes | 900 |
| 1-10 days | 1 hour | 3600 |
| 10-31 days | 1 day | 86400 |
2. DescribeSiteTopData - Top-N Rankings
Query Top-N ranking data by various dimensions.
Limit Options: 5, 10, 150
Available Metrics (FieldName)
| Field | Type | Description |
|---|---|---|
Traffic | int | Response traffic from ESA to client (bytes) |
Requests | int | Number of requests |
RequestTraffic | int | Client request traffic (bytes) |
PageView | int | Page views |
Available Dimensions
Geographic Dimensions: ClientCountryCode (country), ClientProvinceCode (province), ClientISP (ISP), ClientASN
Client Info: ClientIP, ClientIPVersion, ClientBrowser, ClientDevice, ClientOS
Request Details: ClientRequestHost, ClientRequestMethod, ClientRequestPath, ClientRequestProtocol, ClientRequestQuery, ClientRequestReferer, ClientRequestUserAgent
Response/Cache: EdgeCacheStatus, EdgeResponseStatusCode, EdgeResponseContentType, OriginResponseStatusCode
Others: ALL (aggregated), SiteId (account-level query), Version, ClientSSLProtocol, ClientXRequestedWith
Error Handling
| HTTP Code | Error Code | Description |
|---|---|---|
| 400 | InvalidParameter.TimeRange | Time range exceeded (max 31 days) |
| 400 | InvalidEndTime.Mismatch | EndTime earlier than StartTime |
| 400 | InvalidParameter.Field | Invalid field name |
| 400 | InvalidParameter.Dimension | Invalid dimension |
| 400 | InvalidTime.Malformed | Time format error (use yyyy-MM-ddTHH:mm:ssZ) |
Detailed reference: references/time-series.md, references/top-data.md, references/fields.md
Origin Rules — CDN 代理回源
Origin Rules 控制 ESA CDN 如何回源到源站。当需要将子域名通过 ESA CDN 代理到后端服务器时使用。
CDN 代理回源工作流
CreateRecord(A, proxied=true) → ApplyCertificate → CreateOriginRule → 验证API Summary
- Origin Rule 管理:
CreateOriginRule,ListOriginRules,GetOriginRule,UpdateOriginRule,DeleteOriginRule
Key Parameters
| 参数 | 说明 |
|---|---|
rule | 匹配条件,如 (http.host eq "sub.example.com") |
origin_scheme | 回源协议:http 或 https(默认 https) |
origin_port | 回源端口 |
dns_record | 回源目标 DNS 记录名(必须是 ESA DNS 记录名,不能是 IP) |
Critical Gotchas
1. `dns_record` 必须是 ESA DNS 记录名(如 agent.example.com),不能用原始 IP(如 1.2.3.4),否则报 destination_not_found。 2. `origin_scheme` 默认 HTTPS:源站只监听 HTTP 时必须显式设为 http,否则 502。 3. DNS 记录必须 proxied=true:否则流量不经 CDN,Origin Rule 不生效。 4. Edge Routine Route 优先级高于 Origin Rule:同域名有 ER route 时会拦截请求,需先删除 route。
Detailed reference: references/origin-rules.md
Common operation mapping
Site Management
- Create site:
CreateSite - List sites:
ListSites(supportsSiteName,Status,AccessType,Coveragefilters) - Get site details:
GetSite - Delete site:
DeleteSite - Check site name availability:
CheckSiteName - Verify site ownership:
VerifySite - Update access type:
UpdateSiteAccessType - Update coverage:
UpdateSiteCoverage - Get current nameservers:
GetSiteCurrentNS - Update custom nameservers:
UpdateSiteVanityNS - Pause/resume site:
UpdateSitePause,GetSitePause - Site exclusivity:
UpdateSiteNameExclusive,GetSiteNameExclusive - Version management:
ActivateVersionManagement,DeactivateVersionManagement
Site Configuration
- IPv6:
GetIPv6,UpdateIPv6
DNS Records
NS access: full record type support. CNAME access: only CNAME and A/AAAA, proxy must stay enabled.
- Create record:
CreateRecord - List records:
ListRecords(supportsType,RecordName,Proxiedfilters) - Get record:
GetRecord - Update record:
UpdateRecord - Delete record:
DeleteRecord - Batch create:
BatchCreateRecords - Export records:
ExportRecords
Cache Rules
- Create cache rule:
CreateCacheRule - List cache rules:
ListCacheRules - Get cache rule:
GetCacheRule - Update cache rule:
UpdateCacheRule - Delete cache rule:
DeleteCacheRule
Cache rule expression notes (important):
CreateCacheRuleparameters are flat, not a nested JSONRuleobject.- The
Ruleparameter is a match condition expression string. See Rule Expression Syntax section below. - Quick reminders:
ends_with()/starts_with()must use function-call style;matches(regex) requires standard plan or above. - Set edge cache TTL with
--EdgeCacheMode override_origin --EdgeCacheTtl <seconds>.
Rule Expression Syntax
ESA uses a unified rule engine expression syntax across multiple features (cache rules, WAF custom rules, rate limiting, URL rewrite, header modification, etc.).
When to use
Use this syntax for the Rule parameter in any ESA API that accepts a match condition expression:
CreateCacheRule/UpdateCacheRule- Cache rulesCreateWafRule/UpdateWafRule- WAF custom rulesCreateRatePlanRule- Rate limiting rulesCreateRewriteUrlRule/UpdateRewriteUrlRule- URL rewrite rules- Origin rules, redirect rules, header modification rules, etc.
Expression format
(condition)
(condition1 and condition2)
(condition1) or (condition2)Max nesting depth: 2 levels.
Operator syntax - two styles
Infix style (operator between field and value):
(field eq "value")
(field ne "value")
(field contains "value")
(field in {"value1" "value2"})
(field matches "regex")Function style (operator wraps field):
(starts_with(field, "value"))
(ends_with(field, "value"))
(exists(field))
(len(field) gt 100)
(lower(field) eq "value")Common patterns
# Match file extension
--Rule '(http.request.uri.path.extension eq "html")'
# Match multiple extensions
--Rule '(http.request.uri.path.extension in {"js" "css" "png" "jpg"})'
# Match URL prefix
--Rule '(starts_with(http.request.uri, "/api/"))'
# Match URL suffix
--Rule '(ends_with(http.request.uri, ".html"))'
# Match URL containing substring (value MUST start with /)
--Rule '(http.request.uri contains "/test")'
# Match specific host
--Rule '(http.host eq "www.example.com")'
# Combined conditions
--Rule '(http.request.uri contains "/test" and ip.geoip.country eq "CN")'
# Match by country
--Rule '(ip.geoip.country eq "CN")'
# Exclude path
--Rule '(not starts_with(http.request.uri, "/admin/"))'
# Negating set membership
--Rule '(not http.host in {"a.com" "b.com"})'Key Gotchas
1. ends_with and starts_with must use function-call syntax, NOT infix. 2. matches (regex) requires standard plan or above; basic plan returns RuleRegexQuotaCheckFailed. 3. contains with URI must include path separator: "/test" is correct; "test" alone causes CompileRuleError. 4. List values in in operator are space-separated inside braces: {"a.com" "b.com"}. 5. Outer parentheses are optional for single conditions. 6. Use ne for "not equal", never use not...eq. 7. Use not...in for negating set membership (not before field), not not in.
Plan Limitations
| Plan | eq/ne/in/starts_with/ends_with | contains | matches (regex) |
|---|---|---|---|
| Basic | Supported | Supported | Not supported |
| Standard | Supported | Supported | Supported |
| Enterprise | Supported | Supported | Supported |
AccessKey priority (must follow, align with README)
1) Environment variables: ALIBABACLOUD_ACCESS_KEY_ID / ALIBABACLOUD_ACCESS_KEY_SECRET / ALIBABACLOUD_REGION_ID Region policy: ALIBABACLOUD_REGION_ID is an optional default. If unset, decide the most reasonable region for the task; if unclear, ask the user. 2) Shared config file: ~/.alibabacloud/credentials (region still from env)
Auth setup (README-aligned)
Environment variables:
export ALIBABACLOUD_ACCESS_KEY_ID="your-ak"
export ALIBABACLOUD_ACCESS_KEY_SECRET="your-sk"
export ALIBABACLOUD_REGION_ID="cn-hangzhou"Also supported by the Alibaba Cloud SDKs:
export ALIBABA_CLOUD_ACCESS_KEY_ID="your-ak"
export ALIBABA_CLOUD_ACCESS_KEY_SECRET="your-sk"Legacy compatibility:
export ALICLOUD_ACCESS_KEY_ID="your-ak"
export ALICLOUD_ACCESS_KEY_SECRET="your-sk"Shared config file:
~/.alibabacloud/credentials
[default]
type = access_key
access_key_id = your-ak
access_key_secret = your-skAPI discovery
- Product code:
ESA - Default API version:
2024-09-10 - Metadata endpoint:
https://api.aliyun.com/meta/v1/products/ESA/versions/2024-09-10/api-docs.json - Use OpenAPI metadata endpoints to list APIs and get schemas (see references).
Output policy
If you need to save responses or generated artifacts, write them under: output/aliyun-esa-manage/
References
Pages, ER & KV
- Pages Deployment Reference:
references/pages.md - Edge Routine Reference:
references/er.md - Edge KV Storage Reference:
references/kv.md
Origin Rules
- Origin Rule configuration & troubleshooting:
references/origin-rules.md
Site Management
- API overview:
references/api_overview.md - Endpoints:
references/endpoints.md - Sites:
references/sites.md - DNS records:
references/dns-records.md - Cache:
references/cache.md - Sources:
references/sources.md - Rule expression - generation guide:
references/rule-generation-guide.md - Rule expression - match fields:
references/rule-match-fields.md - Rule expression - operators:
references/rule-operators.md - Rule expression - examples:
references/rule-examples.md
Analytics
- Time-Series Data API:
references/time-series.md - Top-N Data API:
references/top-data.md - Metrics and Dimensions Reference:
references/fields.md
interface:
display_name: "Alibaba Cloud Network ESA"
short_description: "Edge security acceleration workflows"
default_prompt: "Use $aliyun-esa-manage to complete this network/esa task on Alibaba Cloud."
ESA OpenAPI overview (2024-09-10) - Pages, ER, KV, Site Management, DNS & Cache
API index for Pages deployment, Edge Routine, Edge KV, site management, configuration, DNS records, and cache rules. All APIs via Python SDK (alibabacloud_esa20240910).
Pages (Based on Edge Routine)
Underlying ER API calls, see references/pages.md for complete flow.
HTML Deployment Core APIs
- CreateRoutine - Create edge function
- GetRoutineStagingCodeUploadInfo - Get code upload OSS signature
- CommitRoutineStagingCode - Submit code version
- PublishRoutineCodeVersion - Publish to staging/production
- GetRoutine - Get access URL
Static Directory Deployment Core APIs
- CreateRoutine - Create edge function
- CreateRoutineWithAssetsCodeVersion - Create code version with assets
- GetRoutineCodeVersionInfo - Query version build status
- CreateRoutineCodeDeployment - Deploy by percentage to specified environment
- GetRoutine - Get access URL
Edge Routine (ER)
Function Management
- CreateRoutine - Create edge function
- DeleteRoutine - Delete edge function
- GetRoutine - Get edge function details
- GetRoutineUserInfo - Get user edge function info
- ListUserRoutines - Paginate all edge functions
Code Version
- GetRoutineStagingCodeUploadInfo - Get code upload info
- CommitRoutineStagingCode - Submit staging code version
- PublishRoutineCodeVersion - Publish code version to staging/production
- DeleteRoutineCodeVersion - Delete code version
- CreateRoutineWithAssetsCodeVersion - Create code version with assets (for static directory deployment)
- GetRoutineCodeVersionInfo - Get code version status
- CreateRoutineCodeDeployment - Create code deployment (for assets deployment)
- ListRoutineCodeVersions - Paginate code versions
- GetRoutineCodeVersion - Query single code version details
- UpdateRoutineConfigDescription - Update function description
Route Management
- CreateRoutineRoute - Create function route
- DeleteRoutineRoute - Delete function route
- GetRoutineRoute - Get route details
- UpdateRoutineRoute - Update function route
- ListRoutineRoutes - List function routes
- ListSiteRoutes - List site routes
Related Record Management
- CreateRoutineRelatedRecord - Create function related record (domain)
- DeleteRoutineRelatedRecord - Delete function related record
- ListRoutineRelatedRecords - List function related records
Edge KV
Edge key-value storage, supports Namespace and Key-Value management.
Namespace Management
- CreateKvNamespace - Create KV storage space
- DeleteKvNamespace - Delete KV storage space
- GetKvNamespace - Query single namespace info
- GetKvAccount - Query account KV usage info and all namespaces
- DescribeKvAccountStatus - Query if Edge KV is enabled
Single Key Operations
- PutKv - Write key-value pair (≤2MB)
- PutKvWithHighCapacity - Write large capacity key-value pair (≤25MB)
- GetKv - Read key's value
- GetKvDetail - Read key-value and TTL info
- DeleteKv - Delete key-value pair
Batch Operations
- BatchPutKv - Batch write key-value pairs (≤2MB)
- BatchPutKvWithHighCapacity - Batch write large capacity (≤100MB)
- BatchDeleteKv - Batch delete key-value pairs (≤10000)
- BatchDeleteKvWithHighCapacity - Batch delete large capacity (≤100MB)
- ListKvs - List all keys under namespace (supports prefix filter and pagination)
Site Management
- CreateSite - Add site
- ListSites - List sites (supports pagination and filters)
- GetSite - Get site details
- DeleteSite - Delete site
- CheckSiteName - Check site name availability
- VerifySite - Verify site ownership
- UpdateSiteAccessType - Update access type (CNAME/NS)
- UpdateSiteCoverage - Update coverage area
- GetSiteCurrentNS - Get current NS servers
- UpdateSiteVanityNS - Update custom NS
- UpdateSitePause - Pause/resume site proxy
- GetSitePause - Get site proxy status
- UpdateSiteNameExclusive - Set site exclusive
- GetSiteNameExclusive - Get site exclusive status
- ActivateVersionManagement - Enable version management
- DeactivateVersionManagement - Disable version management
Site Configuration
- GetIPv6 - Get IPv6 config
- UpdateIPv6 - Update IPv6 config
DNS Records
NS access: supports all record types. CNAME access: only CNAME and A/AAAA, and proxy (acceleration) cannot be disabled.
- CreateRecord - Create DNS record
- ListRecords - List DNS records (supports Type, RecordName, Proxied filters)
- GetRecord - Get DNS record details
- UpdateRecord - Update DNS record
- DeleteRecord - Delete DNS record
- BatchCreateRecords - Batch create DNS records
- ExportRecords - Export DNS records
Cache Rules
- CreateCacheRule - Create cache rule
- ListCacheRules - List cache rules
- GetCacheRule - Get cache rule details
- UpdateCacheRule - Update cache rule
- DeleteCacheRule - Delete cache rule
References
- Official API list: https://next.api.aliyun.com/document/ESA/2024-09-10/overview
- API metadata: https://api.aliyun.com/meta/v1/products/ESA/versions/2024-09-10/api-docs.json
ESA Cache
Cache rules for ESA sites.
Cache Rules
Cache rules control how content is cached at ESA edge nodes.
Common operations
- Create:
CreateCacheRule - List:
ListCacheRules - Query:
GetCacheRule - Update:
UpdateCacheRule - Delete:
DeleteCacheRule
Rule types
global: Site-wide default cache configurationrule: Conditional rule with match expression
CreateCacheRule parameters
Parameters are flat (not a nested JSON object). Key parameters:
Required:
SiteId: Site ID (Long)Rule: Match condition expression (String). For rule expression syntax, see `rule-generation-guide.md` and related rule reference files in this directory.
Optional:
RuleName: Rule name (not required forglobaltype)RuleEnable:on/off(not required forglobaltype)EdgeCacheMode: Edge cache modefollow_origin: Follow origin cache policy (default)no_cache: Do not cacheoverride_origin: Override origin cache policyfollow_origin_bypass: Follow origin if exists, otherwise no cacheEdgeCacheTtl: Edge cache TTL in seconds (e.g. 864000 = 10 days)BrowserCacheMode: Browser cache mode (no_cache,follow_origin,override_origin)BrowserCacheTtl: Browser cache TTL in secondsQueryStringMode: Query string handling (ignore_all,exclude_query_string,reserve_all,include_query_string)BypassCache: Bypass cache mode (cache_all,bypass_all)ServeStale: Serve stale cache when origin unavailable (on/off)SortQueryStringForCache: Sort query string for cache key (on/off)
Rule expression syntax
See rule expression reference files in this directory for complete documentation:
rule-generation-guide.md- Generation guide from natural languagerule-match-fields.md- All match fields (HTTP, IP/Geo, Map fields)rule-operators.md- All operators (string/numeric comparison, function-style, negation, logical)rule-examples.md- Scenario examples and common mistakes
Quick reminder for cache rules:
- Parameters are flat (not a nested JSON
Ruleobject). ends_with/starts_withuse function-call syntax:(ends_with(http.request.uri, ".html")).matches(regex) requires standard plan or above.
Cache TTL options
- Override origin: set
EdgeCacheModetooverride_origin+EdgeCacheTtlin seconds - Follow origin: set
EdgeCacheModetofollow_origin - Browser cache: set
BrowserCacheMode+BrowserCacheTtl - Common TTL values: 3600 (1h), 86400 (1d), 604800 (7d), 864000 (10d), 2592000 (30d)
Tiered Cache
Multi-level cache architecture configuration.
GetTieredCache: Get current tiered cache configUpdateTieredCache: Update tiered cache config
Cache architecture modes:
edge_regional: Regional edge cachingedge_smart: Smart edge caching
Cache Tag
Cache tag configuration for granular cache control.
GetCacheTag: Get cache tag configUpdateCacheTag: Update cache tag config
References
- CreateCacheRule: https://help.aliyun.com/zh/esa/developer-reference/api-esa-2024-09-10-createcacherule
- ListCacheRules: https://help.aliyun.com/zh/esa/developer-reference/api-esa-2024-09-10-listcacherules
ESA DNS Records
DNS records management for ESA sites.
Access Type & Record Restrictions
- NS access: Full DNS record management via API, supports all record types (A/AAAA, CNAME, MX, TXT, NS, SRV, CAA).
- CNAME access: DNS record APIs are available, but with restrictions:
- Only `CNAME` and `A/AAAA` record types are allowed. Other types (MX, TXT, NS, SRV, CAA) will fail.
- Records cannot disable acceleration (proxy must remain enabled, i.e.
Proxiedmust betrue).
Common operations
- Create:
CreateRecord - List:
ListRecords(supports pagination and filters) - Query:
GetRecord - Update:
UpdateRecord - Delete:
DeleteRecord - Batch create:
BatchCreateRecords - Export:
ExportRecords
Supported record types
| Type | NS access | CNAME access |
|---|---|---|
A/AAAA | Supported | Supported (must enable proxy) |
CNAME | Supported | Supported (must enable proxy) |
MX | Supported | Not supported |
TXT | Supported | Not supported |
NS | Supported | Not supported |
SRV | Supported | Not supported |
CAA | Supported | Not supported |
Note: Record type must be exact: use A/AAAA (not just A).
ListRecords filters
Type: Record type (e.g.,A/AAAA,CNAME)RecordName: Record name (fuzzy match)Proxied: Whether proxied through ESA (true/false)
CreateRecord parameters
Required:
SiteId: Site IDRecordName: Must be the full domain name (e.g.,www.example.com), not just the subdomain prefix (e.g.,www). The suffix must match the site name, otherwise returnsInvalidParameter.InvalidRecordNameSuffix.Type: Record typeData: Record value in JSON format (e.g.,{"Value":"1.2.3.4"}for A/AAAA,{"Value":"target.com"}for CNAME)Ttl: Time to live (seconds). Set to1for system-determined TTL.
Conditionally required:
BizName: Required when `Proxied` is `true` (i.e., acceleration enabled). Valid values:web,api,image_video. Omitting it when proxy is on causesInvalidParameter.InvalidBiz.SourceType: Required for CNAME records. Valid values:OSS,S3,LB,OP,Domain. Defaults toDomainif omitted.
Optional:
Proxied: Enable ESA proxy/acceleration (default: false). CNAME-access sites must set this totrue.Priority: Priority for MX/SRV records.HostPolicy: Origin host policy for CNAME records:follow_hostnameorfollow_origin_domain.Comment: Record comment (max 100 characters).
Key gotchas
1. RecordName must be full domain: qodertest.qoder.weiyigirl.top, not qodertest. 2. BizName is required when Proxied=true: Without it, API returns InvalidParameter.InvalidBiz with misleading message "business type is empty or incorrect". 3. CNAME-access sites must enable proxy: Setting Proxied=false on CNAME-access sites will fail.
Behavioral notes
- Record names must be unique within the same type
- Deleting a record that doesn't exist returns success (idempotent)
BatchCreateRecordscan create multiple records of different types at onceExportRecordsreturns all DNS records in BIND zone file format
References
- CreateRecord: https://help.aliyun.com/zh/esa/developer-reference/api-esa-2024-09-10-createrecord
- ListRecords: https://help.aliyun.com/zh/esa/developer-reference/api-esa-2024-09-10-listrecords
ESA OpenAPI endpoints
ESA uses RPC-style OpenAPI. Prefer SDK or OpenAPI Explorer.
Public endpoint
- Default:
esa.cn-hangzhou.aliyuncs.com - Pattern:
esa.<region-id>.aliyuncs.com
Common endpoints
| Region | Endpoint |
|---|---|
| cn-hangzhou | esa.cn-hangzhou.aliyuncs.com |
| cn-shanghai | esa.cn-shanghai.aliyuncs.com |
| cn-beijing | esa.cn-beijing.aliyuncs.com |
| ap-southeast-1 | esa.ap-southeast-1.aliyuncs.com |
Notes
- API version:
2024-09-10 - ESA is a global service; most operations work from any endpoint.
- Use
cn-hangzhouas default if region is unspecified.
References
- Endpoint list: https://www.alibabacloud.com/help/en/esa/developer-reference/endpoints
Edge Routine (ER) — Edge Function Reference
ESA Edge Routine is a serverless edge function service where code runs on global edge nodes. Supports full lifecycle management: creation, code submission, deployment, route configuration, and record management.
Manage Edge Routine via Python SDK calling ESA OpenAPI.
API List
Function Management
| API | Description | Key Parameters |
|---|---|---|
CreateRoutine | Create edge function | Name(required, lowercase letters/numbers/hyphens, >=2 chars), Description(optional) |
DeleteRoutine | Delete edge function | Name(required) |
GetRoutine | Get edge function details, including code version list, related records, default access domain | Name(required) |
GetRoutineUserInfo | Get user edge function overview info | No parameters |
ListUserRoutines | Paginate all edge functions under account | PageNumber, PageSize |
Code Version Management
| API | Description | Key Parameters |
|---|---|---|
GetRoutineStagingCodeUploadInfo | Get signature info for code upload to OSS | Name(required) |
CommitRoutineStagingCode | Submit staging code, generate formal code version | Name(required), CodeDescription(optional) |
PublishRoutineCodeVersion | Publish code version to staging/production | Name(required), Env(required, "staging"/"production"), CodeVersion(required) |
DeleteRoutineCodeVersion | Delete code version | Name(required), CodeVersion(required) |
CreateRoutineWithAssetsCodeVersion | Create code version with assets (for static file deployment) | Name(required), CodeDescription(optional) |
GetRoutineCodeVersionInfo | Get code version status (init/available/failed) | Name(required), CodeVersion(required) |
CreateRoutineCodeDeployment | Deploy code version to specified environment by percentage (for assets deployment) | Name(required), Env(required), Strategy(required), CodeVersions(required, JSON) |
ListRoutineCodeVersions | Paginate function's code versions | Name(required), PageNumber, PageSize |
GetRoutineCodeVersion | Query single code version details | Name(required), CodeVersion(required) |
Route Management
| API | Description | Key Parameters |
|---|---|---|
CreateRoutineRoute | Create route | SiteId(required), Rule(ESA rule expression, e.g. (http.host eq "test.example.com")), RoutineName(required), RouteName(required), RouteEnable("on"/"off"), Bypass("on"/"off"). Note: SDK parameter is rule (not route). Use ESA rule expression syntax. |
UpdateRoutineRoute | Update route configuration | SiteId(required), ConfigId(required), RouteName(required), RouteEnable(required), Rule(required), RoutineName(required), Bypass(required) |
DeleteRoutineRoute | Delete route | SiteId(required), ConfigId(required) |
GetRoutineRoute | Get route details | SiteId(required), ConfigId(required) |
ListRoutineRoutes | List all routes for a function | RoutineName(required), RouteName(optional filter), PageNumber, PageSize |
ListSiteRoutes | List all routes for a site | SiteId(required), RouteName(optional filter), PageNumber, PageSize |
Related Record Management
| API | Description | Key Parameters |
|---|---|---|
CreateRoutineRelatedRecord | Create function related record (domain), triggers function execution | Name(required), SiteId(required), RecordName(required) |
DeleteRoutineRelatedRecord | Delete related record | Name(required), SiteId(required), RecordName(required), RecordId(optional) |
ListRoutineRelatedRecords | List all related records for a function | Name(required), PageNumber, PageSize, SearchKeyWord(optional) |
Standard Workflow
Create and Deploy Edge Function (Complete Flow)
1. CreateRoutine → Create function
2. GetRoutineStagingCodeUploadInfo → Get upload signature
3. Upload code to OSS (POST with signature) → Code upload
4. CommitRoutineStagingCode → Submit code version
5. PublishRoutineCodeVersion(env=staging) → Deploy to staging
6. PublishRoutineCodeVersion(env=production)→ Deploy to production
7. (Optional) CreateRoutineRoute → Bind custom domain route
8. (Optional) CreateRoutineRelatedRecord → Create related record
9. GetRoutine → Get details, obtain default access URLCode Format Requirements
Edge Routine code must export fetch handler:
async function handleRequest(request) {
return new Response("Hello World", {
headers: { "content-type": "text/html;charset=UTF-8" },
});
}
export default {
async fetch(request) {
return handleRequest(request);
},
};Route Pattern Explanation
Route's Rule field uses ESA rule expression, for example:
(http.host eq "test.example.com" and starts_with(http.request.uri.path, "/"))
Simplified path format (e.g. test.example.com/*) needs to be converted to rule expression:
- Domain prefix
*=ends_with(http.host, ".example.com") - Path suffix
*=starts_with(http.request.uri.path, "/")
Python SDK Usage
from alibabacloud_esa20240910.client import Client as Esa20240910Client
from alibabacloud_esa20240910 import models as esa_models
from alibabacloud_tea_openapi import models as open_api_models
import requests
def create_client(region_id: str = "cn-hangzhou") -> Esa20240910Client:
config = open_api_models.Config(
region_id=region_id,
endpoint="esa.cn-hangzhou.aliyuncs.com",
)
return Esa20240910Client(config)
# Create edge function
def create_routine(name: str, description: str = ""):
client = create_client()
request = esa_models.CreateRoutineRequest(name=name, description=description)
return client.create_routine(request)
# List edge functions
def list_routines():
client = create_client()
resp = client.get_routine_user_info()
return resp.body
# Get function details
def get_routine(name: str):
client = create_client()
request = esa_models.GetRoutineRequest(name=name)
return client.get_routine(request)
# Delete edge function
def delete_routine(name: str):
client = create_client()
request = esa_models.DeleteRoutineRequest(name=name)
return client.delete_routine_with_options(request)
# Upload code and deploy (complete flow)
def deploy_code(name: str, code: str, env: str = "production"):
client = create_client()
# 1. Get upload signature
upload_info = client.get_routine_staging_code_upload_info(
esa_models.GetRoutineStagingCodeUploadInfoRequest(name=name)
)
oss_config = upload_info.body.oss_post_config
# 2. Upload code to OSS
form_data = {
"OSSAccessKeyId": oss_config.ossaccess_key_id,
"Signature": oss_config.signature,
"callback": oss_config.callback,
"x:codeDescription": oss_config.x_code_description,
"policy": oss_config.policy,
"key": oss_config.key,
}
requests.post(oss_config.url, data=form_data, files={"file": code.encode()})
# 3. Submit code version
commit_resp = client.commit_routine_staging_code(
esa_models.CommitRoutineStagingCodeRequest(name=name)
)
code_version = commit_resp.body.code_version
# 4. Deploy
client.publish_routine_code_version(
esa_models.PublishRoutineCodeVersionRequest(
name=name, env=env, code_version=code_version
)
)
return code_versionRoute Management
# Create route
def create_route(site_id: int, routine_name: str, route_name: str, rule: str):
client = create_client()
request = esa_models.CreateRoutineRouteRequest(
site_id=site_id,
routine_name=routine_name,
route_name=route_name,
rule=rule,
route_enable="on",
bypass="off",
)
return client.create_routine_route(request)
# List function routes
def list_routine_routes(routine_name: str):
client = create_client()
request = esa_models.ListRoutineRoutesRequest(routine_name=routine_name)
return client.list_routine_routes(request)Related Record Management
# Create related record
def create_related_record(name: str, site_id: int, record_name: str):
client = create_client()
request = esa_models.CreateRoutineRelatedRecordRequest(
name=name, site_id=site_id, record_name=record_name
)
return client.create_routine_related_record(request)This document introduces the field names and their meanings returned when calling ESA data analysis APIs. These fields are data analysis results obtained through API calls, used to support deeper data analysis and business insights. Through these fields, you can get detailed traffic, requests, cache status, and other information to comprehensively understand business performance and system operation status.
Metrics
Data analysis provides you with rich data metric values, including response traffic, request count, request traffic, page views, etc. These metrics can comprehensively show your business performance and help you deeply understand business activity, traffic distribution, user behavior patterns, and system performance. Through these detailed data analyses, you can more accurately evaluate business operation status, timely discover potential issues, and formulate corresponding optimization strategies to improve user experience and business efficiency.
| Field Name | Data Type | Description |
|---|---|---|
| Traffic | int | Size of ESA node response returned to client, unit: Byte |
| Requests | int | Number of requests |
| RequestTraffic | int | Size of client request, unit: Byte |
| PageView | int | Page views |
Dimensions
Data analysis provides multiple dimensions for data metrics, helping you analyze business performance from different angles. Helps you comprehensively understand traffic geographic distribution, user behavior patterns, request details, cache status, and system performance. Multi-dimensional data analysis not only helps optimize business processes and improve user experience, but also helps you quickly locate issues and formulate targeted solutions to better manage business operations.
| Field Name | Description |
|---|---|
| ALL | User dimension full data |
| ClientASN | Autonomous System Number (ASN) information parsed from client IP address |
| ClientBrowser | Client browser type |
| ClientCountryCode | ISO-3166 Alpha-2 Code parsed from client IP address |
| ClientDevice | Client device type |
| ClientIP | Client IP that established connection with ESA node |
| ClientIPVersion | Client IP version that established connection with ESA node |
| ClientISP | ISP information parsed from client IP address |
| ClientOS | Client system model |
| ClientProvinceCode | China mainland province information parsed from client IP address |
| ClientRequestHost | Client request Host information |
| ClientRequestMethod | Client request HTTP Method information |
| ClientRequestPath | Client request path information |
| ClientRequestProtocol | Client request protocol information |
| ClientRequestQuery | Client request Query information |
| ClientRequestReferer | Client request Referer information |
| ClientRequestUserAgent | Client request User-Agent information |
| ClientSSLProtocol | Client SSL protocol version, - indicates no SSL used |
| ClientXRequestedWith | Client X-Requested-With request header |
| EdgeCacheStatus | Cache status of client request |
| EdgeResponseContentType | Content-Type information of ESA node response |
| EdgeResponseStatusCode | Status code returned by ESA node response to client |
| OriginResponseStatusCode | Origin response status code |
| SiteId | Current site ID |
| Version | Version management version number |
Edge KV — Edge Key-Value Storage Reference
ESA Edge KV is a distributed edge key-value storage service, readable and writable in Edge Routine, also manageable via OpenAPI. Suitable for edge configuration distribution, feature flags, A/B testing, and other scenarios.
Core Concepts
- Namespace (Storage Space): Isolation container for KV data, each account can create multiple namespaces
- Key: Key name, max 512 characters, cannot contain spaces or backslashes
- Value: Value, standard API max 2MB, high capacity API max 25MB
- TTL: Optional expiration time, supports absolute timestamp (Expiration) or relative seconds (ExpirationTtl)
Limits
| Limit Item | Value |
|---|---|
| Max Key length | 512 characters |
| Single Value max (PutKv/BatchPutKv) | 2 MB |
| Single Value max (PutKvWithHighCapacity) | 25 MB |
| Batch request body max (BatchPutKvWithHighCapacity/BatchDeleteKvWithHighCapacity) | 100 MB |
| BatchDeleteKv max keys per request | 10,000 |
| Single Namespace max capacity | 1 GB |
| ListKvs pagination limit | PageNumber × PageSize ≤ 50,000 |
API List
Namespace Management
| API | Description | Key Parameters |
|---|---|---|
CreateKvNamespace | Create KV storage space | Namespace(required, string), Description(optional) |
DeleteKvNamespace | Delete KV storage space | Namespace(required, string) |
GetKvNamespace | Query single namespace info | Namespace(required, string) |
GetKvAccount | Query account KV usage info and all namespaces | No parameters |
DescribeKvAccountStatus | Query if Edge KV is enabled | No parameters |
Single Key Operations
| API | Description | Key Parameters |
|---|---|---|
PutKv | Write key-value pair (≤2MB) | Namespace(required), Key(required), Value(body, required), Expiration(optional, Unix timestamp), ExpirationTtl(optional, seconds), Base64(optional, bool) |
PutKvWithHighCapacity | Write large capacity key-value pair (≤25MB) | Same as PutKv, but via SDK body method |
GetKv | Read key's value | Namespace(required), Key(required), Base64(optional, bool) |
GetKvDetail | Read key-value and TTL | Namespace(required), Key(required) |
DeleteKv | Delete key-value pair | Namespace(required), Key(required) |
Batch Operations
| API | Description | Key Parameters |
|---|---|---|
BatchPutKv | Batch write key-value pairs (≤2MB) | Namespace(required), body is JSON array [{Key, Value, Expiration?, ExpirationTtl?}] |
BatchPutKvWithHighCapacity | Batch write large capacity (≤100MB) | Same as above, via SDK body method |
BatchDeleteKv | Batch delete key-value pairs (≤10000) | Namespace(required), body is JSON array ["key1", "key2", ...] |
BatchDeleteKvWithHighCapacity | Batch delete large capacity (≤100MB) | Same as above, via SDK body method |
ListKvs | List all keys in namespace | Namespace(required), Prefix(optional), PageNumber(optional), PageSize(optional, default 20, max 100) |
Python SDK Usage
Installation
pip install alibabacloud_esa20240910 alibabacloud_tea_openapi alibabacloud_credentialsNamespace Management
from alibabacloud_esa20240910.client import Client as Esa20240910Client
from alibabacloud_esa20240910 import models as esa_models
from alibabacloud_tea_openapi import models as open_api_models
def create_client(region_id: str = "cn-hangzhou") -> Esa20240910Client:
config = open_api_models.Config(
region_id=region_id,
endpoint="esa.cn-hangzhou.aliyuncs.com",
)
return Esa20240910Client(config)
# Create namespace
def create_namespace(name: str, description: str = ""):
client = create_client()
request = esa_models.CreateKvNamespaceRequest(
namespace=name,
description=description,
)
return client.create_kv_namespace(request)
# List all namespaces (via GetKvAccount)
def list_namespaces():
client = create_client()
request = esa_models.GetKvAccountRequest()
resp = client.get_kv_account(request)
return resp.body
# Delete namespace
def delete_namespace(name: str):
client = create_client()
request = esa_models.DeleteKvNamespaceRequest(namespace=name)
return client.delete_kv_namespace(request)Key-Value Operations
# Write key-value pair
def put_kv(namespace: str, key: str, value: str, ttl: int = None):
client = create_client()
request = esa_models.PutKvRequest(
namespace=namespace,
key=key,
value=value,
)
if ttl:
request.expiration_ttl = ttl
return client.put_kv(request)
# Read key's value
def get_kv(namespace: str, key: str):
client = create_client()
request = esa_models.GetKvRequest(
namespace=namespace,
key=key,
)
return client.get_kv(request)
# Delete key-value pair
def delete_kv(namespace: str, key: str):
client = create_client()
request = esa_models.DeleteKvRequest(
namespace=namespace,
key=key,
)
return client.delete_kv(request)
# List keys
def list_kvs(namespace: str, prefix: str = None):
client = create_client()
request = esa_models.ListKvsRequest(
namespace=namespace,
prefix=prefix,
)
return client.list_kvs(request)Batch Operations
import json
# Batch write
def batch_put_kv(namespace: str, items: list):
"""items: [{"Key": "k1", "Value": "v1", "ExpirationTtl": 3600}, ...]"""
client = create_client()
request = esa_models.BatchPutKvRequest(
namespace=namespace,
)
# body is JSON string
request.body = json.dumps(items).encode("utf-8")
return client.batch_put_kv(request)
# Batch delete
def batch_delete_kv(namespace: str, keys: list):
"""keys: ["key1", "key2", ...]"""
client = create_client()
request = esa_models.BatchDeleteKvRequest(
namespace=namespace,
)
request.body = json.dumps(keys).encode("utf-8")
return client.batch_delete_kv(request)Using KV in Edge Routine
In Edge Routine code, you need to create an instance via new EdgeKV({namespace: "..."}) to access KV storage (no global instance, must be explicitly created each time):
export default {
async fetch(request) {
const kv = new EdgeKV({ namespace: "my-namespace" });
// Write
await kv.put("key1", "value1");
// Read
const value = await kv.get("key1");
// Delete
await kv.delete("key1");
return new Response(value || "not found");
},
};Common Workflows
1. Initialize KV Storage
DescribeKvAccountStatus → (Enable if not enabled)
CreateKvNamespace → PutKv / BatchPutKv → ListKvs verify2. Configuration Distribution (Edge Config Hot Update)
1. Write config via OpenAPI: PutKv(namespace="config", key="feature-flags", value=json)
2. Edge Routine reads config: new EdgeKV({namespace: "config"}).get("feature-flags")
3. Update config by calling PutKv again, edge nodes sync automatically3. Data Cleanup
ListKvs(prefix="temp-") → Filter keys to delete → BatchDeleteKvCommon Error Codes
| HTTP | Error Code | Description |
|---|---|---|
| 400 | InvalidNameSpace.Malformed | Invalid namespace name (e.g. empty string) |
| 400 | InvalidKey.Malformed | Invalid key name (e.g. empty string) |
| 400 | InvalidKey.ExceedsMaximum | Key length exceeds 512 bytes |
| 400 | InvalidValue.ExceedsMaximum | Value exceeds 2MB (or 25MB) |
| 404 | InvalidNameSpace.NotFound | Namespace does not exist |
| 404 | InvalidKey.NotFound | Key does not exist |
| 406 | InvalidNameSpace.Duplicate | Namespace already exists |
| 406 | InvalidNameSpace.QuotaFull | Namespace quota exceeded |
| 403 | InvalidKey.ExceedsCapacity | Namespace capacity full |
| 429 | TooQuickRequests | Modify/delete operations too frequent |
Origin Rules — 回源规则配置
Origin Rules 控制 ESA CDN 如何回源到源站,包括回源协议、端口、Host 头和 DNS 记录等。
核心概念
- Origin Rule 是一条条件匹配 + 回源行为的规则
- 当请求匹配规则条件时,ESA 按规则指定的方式回源,而非使用站点默认源站
- 常用于:为不同子域名指定不同源站、修改回源协议/端口、自定义 Host 头
API 列表
| 操作 | API | 说明 |
|---|---|---|
| 创建 | CreateOriginRule | 创建回源规则 |
| 查询列表 | ListOriginRules | 列出站点下所有回源规则 |
| 查询详情 | GetOriginRule | 按 config_id 查询 |
| 更新 | UpdateOriginRule | 修改规则 |
| 删除 | DeleteOriginRule | 删除规则 |
关键参数
CreateOriginRule
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
site_id | long | 是 | 站点 ID |
rule | string | 是 | 匹配条件表达式,如 (http.host eq "sub.example.com") |
origin_scheme | string | 否 | 回源协议:http 或 https(默认 https) |
origin_port | int | 否 | 回源端口(默认 80/443) |
origin_host | string | 否 | 回源 Host 头 |
dns_record | string | 否 | 回源目标 DNS 记录名 |
origin_sni | string | 否 | HTTPS 回源时的 SNI |
CDN 代理回源工作流
将子域名通过 ESA CDN 代理到后端服务器的完整流程:
1. CreateRecord → 添加 A 记录 (proxied=true)
2. ApplyCertificate → 申请免费 SSL 证书
3. CreateOriginRule → 配置回源协议/端口/DNS记录
4. 验证访问 → curl -sI https://sub.example.com步骤详解
1. 创建 DNS 记录
req = esa_models.CreateRecordRequest(
site_id=SITE_ID,
record_name='agent.example.com',
type='A/AAAA',
data=esa_models.CreateRecordRequestData(value='1.2.3.4'),
proxied=True, # 必须开启代理,流量经过 ESA 边缘节点
ttl=1
)
resp = client.create_record(req)
record_id = resp.body.record_id注意: data 参数必须使用 CreateRecordRequestData 对象,不能传 dict,否则报 'dict' has no attribute 'validate'。
2. 申请 SSL 证书
req = esa_models.ApplyCertificateRequest(
site_id=SITE_ID,
domains='agent.example.com',
)
resp = client.apply_certificate(req)
# 状态变化: Applying → TOKEN_DEPLOYED → OK3. 创建 Origin Rule
req = esa_models.CreateOriginRuleRequest(
site_id=SITE_ID,
rule='(http.host eq "agent.example.com")',
origin_scheme='http', # 源站仅支持 HTTP 时必须设置
origin_port=10112, # 源站服务端口
dns_record='agent.example.com', # ⚠️ 必须是 ESA DNS 记录名
)
resp = client.create_origin_rule(req)
config_id = resp.body.config_id4. 验证
# 检查 CDN 代理(Server 应为 ESA)
curl -sI https://agent.example.com | head -5
# 直接检查源站(绕过 CDN)
curl -sI http://1.2.3.4:10112 | head -5重要坑点
1. dns_record 必须是 ESA DNS 记录名,不能是原始 IP
# ❌ 错误 — 使用原始 IP 会导致 502 destination_not_found
dns_record='47.117.136.136'
# ✅ 正确 — 使用 ESA DNS 记录名,ESA 内部解析为 A 记录 IP
dns_record='agent.example.com'原因: ESA Origin Rule 的 dns_record 不是直接指定回源 IP,而是引用 ESA 站点内的 DNS 记录名。ESA 通过该记录名查找对应的 A/AAAA 记录值来确定回源地址。
2. origin_scheme 默认是 HTTPS
如果源站只监听 HTTP(无 SSL),必须显式设置 origin_scheme='http',否则 ESA 会用 HTTPS 回源导致 502。
# ❌ 不设置 origin_scheme → ESA 默认 HTTPS 回源 → 源站无 SSL → 502
origin_scheme 未设置
# ✅ 显式指定 HTTP 回源
origin_scheme='http'3. DNS 记录必须 proxied=True
Origin Rule 仅对经过 ESA 边缘节点的流量生效。如果 DNS 记录 proxied=False(DNS only),请求直接到源站 IP,不经过 CDN,Origin Rule 不会被触发。
4. Edge Routine Route 优先级高于 Origin Rule
如果某个域名同时绑定了 Edge Routine route 和 Origin Rule,Edge Routine 会拦截请求。要使用 Origin Rule 回源,必须先删除该域名的 Edge Routine route。
# 删除 Edge Routine route
req = esa_models.DeleteRoutineRouteRequest(
site_id=SITE_ID,
config_id=ROUTE_CONFIG_ID,
)
client.delete_routine_route(req)5. 缓存可能导致旧内容残留
修改 Origin Rule 或切换域名路由后,ESA 边缘节点可能仍缓存旧内容。等待缓存自然过期或通过控制台手动刷新缓存。
与 Edge Routine 的对比
| 维度 | Edge Routine | Origin Rule (CDN 代理) |
|---|---|---|
| 适用场景 | 静态站点、边缘计算 | 动态服务回源 |
| 流量路径 | 请求在边缘节点处理 | 请求经边缘节点转发到源站 |
| SSL | ESA 边缘终止 | ESA 边缘终止,回源可 HTTP |
| 配置方式 | Routine + Route | DNS 记录 + Origin Rule |
| 优先级 | 高(拦截请求) | 低(仅在无 Route 匹配时生效) |
排查清单
当通过 Origin Rule 代理的域名返回异常时:
1. 502 Bad Gateway
- 检查
origin_scheme是否与源站一致(HTTP vs HTTPS) - 检查
origin_port是否正确 - 检查源站服务是否运行中
2. 502 destination_not_found
- 检查
dns_record是否使用 ESA DNS 记录名(不是 IP) - 检查对应的 DNS 记录是否存在
3. 请求仍被 Edge Routine 处理
- 检查是否存在匹配该域名的 Edge Routine route
- 使用
ListRoutineRoutes或ListSiteRoutes查看
4. 返回旧内容
- CDN 缓存未过期,等待或手动刷新
ESA Pages — Edge Page Deployment Reference
ESA Pages provides the ability to quickly deploy HTML pages or static file directories to edge nodes. Built on Edge Routine, deployments are completed via Python SDK calling ESA OpenAPI.
Deploy HTML Pages
Flow
1. CreateRoutine(name) → Create routine (skip if exists)
2. GetRoutineStagingCodeUploadInfo(name) → Get OSS upload signature
3. POST code to OSS → Upload code file
4. CommitRoutineStagingCode(name) → Commit code version
5. PublishRoutineCodeVersion(staging) → Deploy to staging
6. PublishRoutineCodeVersion(production) → Deploy to production
7. GetRoutine(name) → Get defaultRelatedRecord as access URLCode Template
HTML content needs to be wrapped as Edge Routine code:
const html = `<html><body>Hello World</body></html>`;
async function handleRequest(request) {
return new Response(html, {
headers: { "content-type": "text/html;charset=UTF-8" },
});
}
export default {
async fetch(request) {
return handleRequest(request);
},
};Python SDK Example
from alibabacloud_esa20240910.client import Client as Esa20240910Client
from alibabacloud_esa20240910 import models as esa_models
from alibabacloud_tea_openapi import models as open_api_models
import requests
def create_client() -> Esa20240910Client:
config = open_api_models.Config(
region_id="cn-hangzhou",
endpoint="esa.cn-hangzhou.aliyuncs.com",
)
return Esa20240910Client(config)
def deploy_html(name: str, html: str):
"""Deploy HTML page to ESA Pages"""
client = create_client()
# Escape special characters in template string
escaped_html = html.replace("`", "\\`").replace("$", "\\$")
code = f'''const html = `{escaped_html}`;
async function handleRequest(request) {{
return new Response(html, {{
headers: {{ "content-type": "text/html;charset=UTF-8" }},
}});
}}
export default {{
async fetch(request) {{
return handleRequest(request);
}},
}};'''
# 1. Create routine (skip if exists)
try:
client.create_routine(esa_models.CreateRoutineRequest(name=name))
except Exception as e:
if "RoutineNameAlreadyExist" not in str(e):
raise
# 2. Get upload signature
upload_info = client.get_routine_staging_code_upload_info(
esa_models.GetRoutineStagingCodeUploadInfoRequest(name=name)
)
oss = upload_info.body.oss_post_config
# 3. Upload code to OSS
form_data = {
"OSSAccessKeyId": oss.ossaccess_key_id,
"Signature": oss.signature,
"callback": oss.callback,
"x:codeDescription": oss.x_code_description,
"policy": oss.policy,
"key": oss.key,
}
requests.post(oss.url, data=form_data, files={"file": code.encode()})
# 4. Commit code version
commit = client.commit_routine_staging_code(
esa_models.CommitRoutineStagingCodeRequest(name=name)
)
version = commit.body.code_version
# 5-6. Deploy to staging and production
for env in ["staging", "production"]:
client.publish_routine_code_version(
esa_models.PublishRoutineCodeVersionRequest(
name=name, env=env, code_version=version
)
)
# 7. Get access URL
routine = client.get_routine(esa_models.GetRoutineRequest(name=name))
domain = routine.body.default_related_record
return f"https://{domain}" if domain else NoneDeploy Static File Directory
Flow
1. CreateRoutine(name) → Create routine (skip if exists)
2. CreateRoutineWithAssetsCodeVersion(name) → Create assets code version, get OSS signature
3. Package directory as zip → POST zip to OSS → Upload assets
4. Poll GetRoutineCodeVersionInfo(name, version) → Wait for available status
5. CreateRoutineCodeDeployment(staging, 100%) → Deploy to staging
6. CreateRoutineCodeDeployment(production, 100%) → Deploy to production
7. GetRoutine(name) → Get access URLZip Package Structure
The zip package structure created during deployment depends on the project's EDGE_ROUTINE_TYPE, with three cases:
1. JS_ONLY (entry file only)
your-project.zip
└── routine/
└── index.js ← Code bundled by esbuild (or source file directly when --no-bundle)2. ASSETS_ONLY (static resources only)
your-project.zip
└── assets/
├── image.png
├── style.css
└── subdir/
└── data.json ← All files under assets directory, maintaining original structure3. JS_AND_ASSETS (entry file + static resources, most common)
your-project.zip
├── routine/
│ └── index.js ← Dynamic code (bundled JS)
└── assets/
├── image.png
└── ... ← Static resources, maintaining original structureKey Details
index.jscontent source: By default, produced by prodBuild (esbuild) bundling the entry file; if--no-bundleis passed, reads source file directly- Paths under
assets/are relative toassets.directoryin configuration, recursively traversing all subdirectories and files - Zip package is converted to Buffer via
zip.toBuffer()and uploaded to OSS (first get OSS temporary credentials via API, then POST upload), with max 3 retries - Project type determination logic is in
checkEdgeRoutineType, based on whether entry file and assets directory actually exist - Configuration source priority: CLI args >
esa.jsonc/esa.tomlconfig file
Python SDK Example
import os
import zipfile
import io
import time
import json
import requests
def deploy_folder(name: str, folder_path: str, description: str = ""):
"""Deploy static directory to ESA Pages"""
client = create_client()
# 1. Create routine
try:
client.create_routine(
esa_models.CreateRoutineRequest(name=name, description=description)
)
except Exception as e:
if "RoutineNameAlreadyExist" not in str(e):
raise
# 2. Create assets code version
# Note: This API needs to be called via callApi method
from alibabacloud_tea_openapi import models as api_models
params = api_models.Params(
action="CreateRoutineWithAssetsCodeVersion",
version="2024-09-10", protocol="https", method="POST",
auth_type="AK", body_type="json", req_body_type="json",
style="RPC", pathname="/",
)
body = {"Name": name, "CodeDescription": description}
request = api_models.OpenApiRequest(body=body)
runtime = {}
result = client._client.call_api(params, request, runtime)
oss_config = result.get("body", {}).get("OssPostConfig", {})
code_version = result.get("body", {}).get("CodeVersion")
# 3. Package and upload zip
buf = io.BytesIO()
with zipfile.ZipFile(buf, "w", zipfile.ZIP_DEFLATED) as zf:
for root, dirs, files in os.walk(folder_path):
for f in files:
full = os.path.join(root, f)
rel = os.path.relpath(full, folder_path).replace(os.sep, "/")
zf.write(full, f"assets/{rel}")
buf.seek(0)
form_data = {
"OSSAccessKeyId": oss_config["OSSAccessKeyId"],
"Signature": oss_config["Signature"],
"policy": oss_config["Policy"],
"key": oss_config["Key"],
}
if oss_config.get("XOssSecurityToken"):
form_data["x-oss-security-token"] = oss_config["XOssSecurityToken"]
requests.post(oss_config["Url"], data=form_data, files={"file": buf.getvalue()})
# 4. Wait for version ready
for _ in range(300):
info = client._client.call_api(
api_models.Params(
action="GetRoutineCodeVersionInfo", version="2024-09-10",
protocol="https", method="GET", auth_type="AK",
body_type="json", req_body_type="json", style="RPC", pathname="/",
),
api_models.OpenApiRequest(query={"Name": name, "CodeVersion": code_version}),
{},
)
status = info.get("body", {}).get("Status", "").lower()
if status == "available":
break
if status not in ("", "init"):
raise RuntimeError(f"Build failed: {status}")
time.sleep(1)
# 5-6. Deploy
for env in ["staging", "production"]:
client._client.call_api(
api_models.Params(
action="CreateRoutineCodeDeployment", version="2024-09-10",
protocol="https", method="POST", auth_type="AK",
body_type="json", req_body_type="json", style="RPC", pathname="/",
),
api_models.OpenApiRequest(query={
"Name": name, "Env": env, "Strategy": "percentage",
"CodeVersions": json.dumps([{"Percentage": 100, "CodeVersion": code_version}]),
}),
{},
)
# 7. Get access URL
routine = client.get_routine(esa_models.GetRoutineRequest(name=name))
domain = routine.body.default_related_record
return f"https://{domain}" if domain else NoneCommon Use Cases
1. Deploy Single HTML Page
Suitable for quick prototypes, games, demo pages:
url = deploy_html("game-2048", "<html><body>...</body></html>")
print(f"Access URL: {url}")2. Deploy Frontend Build Output
Suitable for React/Vue/Angular frontend projects' dist/build directories:
url = deploy_folder("my-app", "/path/to/dist")
print(f"Access URL: {url}")Bind Custom Domain (CNAME-Access Sites)
After deploying to Pages, the routine gets a default domain like {name}.{hash}.er.aliyun-esa.net. To use a custom domain (e.g. agent.example.com) on a CNAME-access ESA site, follow this flow:
Flow
1. CreateRecord(A/AAAA, proxied=true) → Register domain in ESA CDN, get record_cname
2. CreateRoutineRoute(rule expression) → Route matching traffic to Edge Routine
3. External DNS: CNAME → record_cname → Point domain to ESA CDN
4. ApplyCertificate(lets_encrypt) → Provision SSL certificate
5. Wait for certificate status → OK → HTTPS readyStep-by-Step
1. Create ESA DNS Record
Create an A/AAAA record with proxy enabled so ESA CDN accepts traffic for this domain:
data_obj = esa_models.CreateRecordRequestData(value="<any-origin-ip>")
req = esa_models.CreateRecordRequest(
site_id=SITE_ID,
record_name="agent.example.com", # Must be full domain name
type="A/AAAA",
data=data_obj,
ttl=1,
proxied=True,
biz_name="web",
)
resp = client.create_record_with_options(req, runtime)
# resp.body.record_id → save for referenceAfter creation, list records to get the record_cname (e.g. agent.example.com.a1.initaf.com):
records = client.list_records(esa_models.ListRecordsRequest(site_id=SITE_ID))
for r in records.body.records:
print(f"{r.record_name} → CNAME: {r.record_cname}")2. Create Edge Routine Route
Create a route that intercepts traffic for this domain and forwards it to the Edge Routine:
req = esa_models.CreateRoutineRouteRequest(
site_id=SITE_ID,
rule='(http.host eq "agent.example.com")', # ESA rule expression
routine_name="my-routine",
route_name="agent-route",
route_enable="on",
bypass="off",
)
resp = client.create_routine_route(req)
# resp.body.config_id → save for referenceImportant: The parameter is rule (ESA rule expression), NOT route. Use (http.host eq "domain") syntax.
3. Update External DNS
At your DNS provider (e.g. Alibaba Cloud DNS / alidns), add a CNAME record:
agent CNAME agent.example.com.a1.initaf.comThe CNAME target follows the pattern: {record_name}.{cname_zone} — get the exact value from record_cname in step 1.
If a wildcard A record (* → some-ip) exists, add an explicit CNAME record for the subdomain to override it.
# Example using alidns SDK
from alibabacloud_alidns20150109.client import Client as DnsClient
from alibabacloud_alidns20150109 import models as dns_models
dns_client = DnsClient(config)
dns_client.add_domain_record(dns_models.AddDomainRecordRequest(
domain_name="example.com",
rr="agent",
type="CNAME",
value="agent.example.com.a1.initaf.com",
))4. Apply SSL Certificate
ESA does not auto-provision SSL certificates for new records. You must request one:
resp = client.apply_certificate(esa_models.ApplyCertificateRequest(
site_id=SITE_ID,
domains="agent.example.com",
type="lets_encrypt",
))Certificate provisioning takes 1-5 minutes. Check status:
certs = client.list_certificates(esa_models.ListCertificatesRequest(site_id=SITE_ID))
for c in certs.body.result:
print(f"{c.common_name} status={c.status}")
# status: Applying → OKDuring provisioning, HTTPS returns TLS internal error. HTTP works immediately.
Key Gotchas
1. Do NOT use `CreateRoutineRelatedRecord` for CNAME-access sites — it creates an internal binding but does NOT create a visible DNS record in ESA, so CDN cannot match the domain and falls back to origin. 2. You need BOTH an ESA DNS record AND a Route — the DNS record makes CDN accept traffic; the Route redirects it to the Edge Routine instead of origin. 3. `CreateRoutineRoute` uses `rule` parameter (ESA rule expression like (http.host eq "domain")), not route. The SDK model parameter is rule, not route. 4. SSL certificate must be manually applied — unlike some CDN services, ESA does not auto-provision certs for new DNS records. 5. DNS record conflicts — if you create both a regular DNS record and a related record for the same domain, you get DependedByOthers error. Delete one before creating the other. 6. Route propagation delay — routes may take 3-5 minutes to propagate to all edge nodes. During this time, traffic still goes to origin. HTTP verification may show Server: ESA before HTTPS works.
Complete Example
def bind_custom_domain(client, site_id, routine_name, domain, route_name):
"""Bind a custom domain to an ESA Pages/ER routine (CNAME-access site)"""
from alibabacloud_tea_util import models as util_models
runtime = util_models.RuntimeOptions()
# 1. Create ESA DNS record
data_obj = esa_models.CreateRecordRequestData(value="127.0.0.1")
client.create_record_with_options(
esa_models.CreateRecordRequest(
site_id=site_id, record_name=domain,
type="A/AAAA", data=data_obj, ttl=1,
proxied=True, biz_name="web",
), runtime,
)
# 2. Create route
client.create_routine_route(esa_models.CreateRoutineRouteRequest(
site_id=site_id,
rule=f'(http.host eq "{domain}")',
routine_name=routine_name,
route_name=route_name,
route_enable="on", bypass="off",
))
# 3. Apply SSL certificate
client.apply_certificate(esa_models.ApplyCertificateRequest(
site_id=site_id, domains=domain, type="lets_encrypt",
))
# 4. Get CNAME target for external DNS
records = client.list_records(esa_models.ListRecordsRequest(
site_id=site_id, record_name=domain.split(".")[0],
))
for r in (records.body.records or []):
if r.record_name == domain:
return r.record_cname # → set this as CNAME in external DNSNotes
1. Function name rules: Only lowercase letters, numbers, hyphens; must start with lowercase letter; length >= 2 2. Same name function: If function exists, reuses existing function and deploys new version code 3. Deployment environments: Deploys to both staging and production by default 4. Access URL: After successful deployment, get default access domain via defaultRelatedRecord from GetRoutine 5. Static directory deployment: Directory cannot be empty; files in zip are placed under assets/ prefix 6. HTML escaping: When wrapping as ER code, escape backticks and $ symbols 7. Assets deployment: CreateRoutineWithAssetsCodeVersion and CreateRoutineCodeDeployment need to be called via callApi method (not directly wrapped by SDK) 8. callApi runtime parameter: Must pass util_models.RuntimeOptions() object, NOT an empty dict {}. Using {} causes AttributeError: 'dict' object has no attribute 'key'
ESA Rule Expression - Scenario Examples
Practical examples for common ESA rule expression scenarios.
Static Resource Caching
# Cache all images (single extension)
--Rule '(http.request.uri.path.extension eq "jpg")'
# Cache multiple static resource types
--Rule '(http.request.uri.path.extension in {"js" "css" "png" "jpg" "gif" "ico" "woff2" "svg" "webp"})'
# Cache specific file
--Rule '(http.request.uri.path.full_file_name eq "index.html")'
# Cache all HTML pages
--Rule '(ends_with(http.request.uri, ".html"))'URL Path Matching
# Match URL prefix (API path)
--Rule '(starts_with(http.request.uri, "/api/"))'
# Match URL suffix
--Rule '(ends_with(http.request.uri, ".json"))'
# Match URL containing substring (MUST start with /)
--Rule '(http.request.uri contains "/test")'
--Rule '(http.request.uri.path contains "/dynamic")'
# Exclude admin path from caching
--Rule '(not starts_with(http.request.uri, "/admin/"))'
# Exclude multiple paths
--Rule '(not starts_with(http.request.uri, "/admin/") and not starts_with(http.request.uri, "/internal/"))'Domain / Host Matching
# Match specific hostname
--Rule '(http.host eq "static.example.com")'
# Match multiple domains
--Rule '(http.host in {"cdn1.example.com" "cdn2.example.com" "cdn3.example.com"})'
# Match domain suffix (e.g. all .cn domains) - requires standard plan
--Rule '(ends_with(http.host, ".cn"))'
# Match domain prefix
--Rule '(starts_with(http.host, "api."))'
# Exclude specific domain
--Rule '(http.host ne "internal.example.com")'Geo / IP Matching
# Match by country
--Rule '(ip.geoip.country eq "CN")'
# Match multiple countries
--Rule '(ip.geoip.country in {"CN" "JP" "KR"})'
# Match by continent (Asia)
--Rule '(ip.geoip.continent eq "AS")'
# Match by AS number
--Rule '(ip.geoip.asnum eq 45104)'
# Match IPv6 traffic
--Rule '(ip.src.version eq "IPv6")'Request Method Matching
# Match GET requests only
--Rule '(http.request.method eq "GET")'
# Match POST requests
--Rule '(http.request.method eq "POST")'
# Match non-GET requests
--Rule '(http.request.method ne "GET")'Header / Cookie / Query Matching
# Match if authorization header exists
--Rule '(exists(http.request.headers["authorization"]))'
# Match if specific cookie exists
--Rule '(exists(http.request.cookies["session"]))'
# Match if query parameter exists
--Rule '(exists(http.request.uri.args["nocache"]))'
# Match by cookie length
--Rule '(len(http.request.cookies["session"]) gt 1024)'
# Match if no authorization header (unauthenticated)
--Rule '(not exists(http.request.headers["authorization"]))'Combined Conditions
# Specific domain + static resources
--Rule '(http.host eq "www.example.com" and http.request.uri.path.extension in {"js" "css" "png"})'
# Specific domain + path prefix
--Rule '(http.host eq "www.example.com" and starts_with(http.request.uri, "/static/"))'
# Static resources but exclude admin
--Rule '(http.request.uri.path.extension in {"js" "css" "png" "jpg"} and not starts_with(http.request.uri, "/admin/"))'
# GET requests from China to HTML pages
--Rule '(http.request.method eq "GET" and ip.geoip.country eq "CN" and ends_with(http.request.uri, ".html"))'
# HTTPS only + specific domain
--Rule '(ssl eq true and http.host eq "secure.example.com")'OR Conditions (Multiple Rule Groups)
# Match either domain
--Rule '(http.host eq "a.example.com") or (http.host eq "b.example.com")'
# Match API path OR static resources
--Rule '(starts_with(http.request.uri, "/api/")) or (http.request.uri.path.extension in {"js" "css" "png"})'
# Match China OR Japan traffic
--Rule '(ip.geoip.country eq "CN") or (ip.geoip.country eq "JP")'Case-Insensitive Matching
# Case-insensitive URL match (use lower() wrapper)
--Rule '(lower(http.request.uri) eq "/readme.html")'
# Case-insensitive host match
--Rule '(lower(http.host) eq "www.example.com")'Regex Examples (Standard Plan and Above)
# Match versioned API paths
--Rule '(http.request.uri matches "^/api/v[0-9]+")'
# Match image files by extension
--Rule '(http.request.uri matches "\\.(jpg|png|gif|webp)$")'
# Match subdomains pattern
--Rule '(http.host matches "^(www|blog|docs)\\.example\\.com$")'ESA Rule Expression - Generation Guide
This guide provides detailed rules for generating correct ESA rule expressions from natural language requirements.
Operator Usage Rules (CRITICAL)
Forbidden Operator Formats
These formats will cause errors - NEVER USE THEM:
| Forbidden Format | Correct Alternative |
|---|---|
not...eq | Use ne instead |
not...ne | Use eq instead |
not in | Use not...in (with space) |
not contains | Use not...contains (with space) |
not matches | Use not...matches (with space) |
Operator Selection by User Expression
| User Expression | Operator | Example |
|---|---|---|
| "equals/is/为" | eq | http.host eq "example.com" |
| "not equals/is not/不等于" | ne | http.host ne "test.com" |
| "contains the following/包含以下各项" | in + set | http.host in {"a.com" "b.com"} |
| "does not contain the following/不包含以下各项" | not...in + set | not http.host in {"a.com" "b.com"} |
| "contains/包含" (substring) | contains | http.request.uri contains "/test" |
| "does not contain/不包含" (substring) | not...contains | not http.request.uri contains "/test" |
| "starts with/以...开头" | starts_with | starts_with(http.request.uri, "/api") |
| "does not start with/不以...开头" | not starts_with | not starts_with(http.request.uri, "/api") |
| "ends with/以...结尾" | ends_with | ends_with(http.request.uri, ".html") |
| "does not end with/不以...结尾" | not ends_with | not ends_with(http.request.uri, ".html") |
Rule Type Recognition
Priority 1: Explicit Context
If user mentions rule type explicitly ("cache rule", "HTTPS rule", "version management"), use that type.
Priority 2: Keywords Inference
| Keywords | Rule Type |
|---|---|
| cache, 缓存, expire, ttl | Cache Rules |
| compress, 压缩, gzip, brotli | Compression Rules |
| HTTPS, SSL/TLS, certificate | HTTPS Rules |
| redirect, 重定向, 301, 302 | Redirect Rules |
| request header, 请求头 | Request Header Modification |
| response header, 响应头 | Response Header Modification |
| rewrite, 重写, url rewrite | URL Rewrite Rules |
| load balance, 负载均衡 | Load Balancing Rules |
| version, 版本, A/B test, 灰度 | Version Management |
| origin, 回源 | Origin Rules |
Priority 3: Default
If unable to determine, default to Node Rules (most versatile).
Rule Type Field Restrictions
| Rule Type | Allowed Fields |
|---|---|
| Node Rules | All common fields |
| Load Balancing | Node fields + ip.src.region_code + http.request.timestamp.sec |
| HTTPS Rules | Only http.host (only eq/ne supported) |
| Version Management | Node fields, but no in_list/not in_list; matches quota-limited |
URI Field Selection Rule
Choose the correct URI field based on match value format:
| Match Value Format | Use Field |
|---|---|
Starts with http:// or https:// (full URL) | http.request.full_uri |
Starts with / (path only) | http.request.uri |
| Path without protocol | http.request.uri |
Examples:
# Full URL matching
http.request.full_uri eq "http://example.com/api/v1/users"
# Path only matching
http.request.uri eq "/api/v1/users"
http.request.uri contains "/test"IMPORTANT: When using contains with http.request.uri, the value MUST start with `/`:
- CORRECT:
http.request.uri contains "/test" - WRONG:
http.request.uri contains "test"(will causeCompileRuleError)
Value Types and Literals
| Type | Format | Example |
|---|---|---|
| String | Double quotes, escape \\ \" | "example.com" |
| Integer | No quotes | 30, 45104 |
| Boolean | Direct use | ssl, not ssl |
| IP | Single or CIDR | 192.168.1.1, 192.168.0.0/24 |
| Set | Space-separated in braces | {"a.com" "b.com" "c.com"} |
| Object | Subscript access | http.request.headers["x-header"] |
Special Values
- Match all requests: Return
true(boolean, not string) - HTTP version values:
HTTP/1.0,HTTP/1.1,HTTP/2.0,HTTP/3.0 - `ip.geoip.asnum`: Integer, no quotes
Logical Operators
- Priority:
not>and>or - Use parentheses to eliminate ambiguity
- Max nesting: 2 levels
Combination examples:
# AND combination
(http.host eq "example.com" and starts_with(http.request.uri, "/api"))
# OR combination
(http.host eq "a.com") or (http.host eq "b.com")
# Complex combination
(http.request.uri contains "/test" and ip.geoip.country eq "CN")
# Negation with set
(http.host contains "example.com") and (not http.host in {"sub1.example.com" "sub2.example.com"})Error Examples and Corrections
Error 1: Using not...eq
# Input: Requests that are NOT m3u8 files
# WRONG: (not http.request.uri.path.extension eq "m3u8")
# CORRECT: http.request.uri.path.extension ne "m3u8"Error 2: Using not in (wrong syntax)
# Input: Exclude jpg and png files
# WRONG: http.request.uri.path.extension not in {"jpg" "png"}
# CORRECT: not http.request.uri.path.extension in {"jpg" "png"}Error 3: Using not contains (wrong syntax)
# Input: User-Agent not containing "bot"
# WRONG: http.user_agent not contains "bot"
# CORRECT: not http.user_agent contains "bot"Generation Flow
1. Preprocess input: Extract match conditions, ignore configuration operations 2. Check "match all": If "all requests", return true 3. Identify rule type: Determine type by keywords and context 4. Filter allowed fields: Based on rule type 5. Select field and operator: Based on user expression and field support 6. Apply URI field rule: Choose http.request.uri or http.request.full_uri 7. Type matching: String with quotes, integer without quotes 8. Combine expression: Use parentheses, prefer function-style for prefix/suffix 9. Error check: Field range, operator restrictions, type constraints
Final Checklist
Before output, verify:
1. ✅ Correct operator format (no not...eq, not in, not contains) 2. ✅ Correct field names and syntax 3. ✅ Correct value types (strings quoted, integers unquoted) 4. ✅ Correct set syntax {value1 value2} 5. ✅ Correct parentheses grouping and logical operators 6. ✅ Correct URI field selection based on value format
ESA Rule Expression - Match Fields
Complete list of match fields available in ESA rule expressions.
Standard Fields (HTTP Request)
| Field | Type | Supported Operators | Example |
|---|---|---|---|
http.host | String | in, eq, ne, contains, matches, starts_with, ends_with, in_list | http.host in {"example.com"} |
http.cookie | String | in, eq, ne, contains, matches | http.cookie contains "PHPSESSID" |
http.referer | String | in, eq, ne, contains, matches | http.referer contains "google.com" |
http.request.method | String | in, eq, ne | http.request.method in {"GET" "POST"} |
http.request.uri | String | in, eq, ne, contains, matches, starts_with, ends_with | http.request.uri contains "/test" |
http.request.uri.path | String | in, eq, ne, contains, matches, starts_with, ends_with | starts_with(http.request.uri.path, "/api") |
http.request.uri.path.extension | String | in, eq, ne | http.request.uri.path.extension in {"jpg" "png"} |
http.request.uri.path.file_name | String | in, eq, ne | http.request.uri.path.file_name eq "index" |
http.request.uri.path.full_file_name | String | in, eq, ne | http.request.uri.path.full_file_name eq "index.html" |
http.request.uri.query | String | in, eq, ne, contains, matches, starts_with, ends_with | http.request.uri.query contains "utm_source" |
http.request.full_uri | String | in, eq, ne, contains, matches, starts_with, ends_with | http.request.full_uri eq "http://example.com/api" |
http.request.version | String | in, eq, ne | http.request.version eq "HTTP/2.0" |
http.request.scheme | String | eq | http.request.scheme eq "https" |
http.user_agent | String | in, eq, ne, contains, matches | http.user_agent contains "bot" |
http.x_forwarded_for | String | in, eq, ne, contains, matches | http.x_forwarded_for eq "192.168.1.1" |
http.request.body.mime | String | eq, ne | http.request.body.mime eq "application/json" |
http.request.timestamp.sec | Integer | eq, ne, le, ge, lt, gt | http.request.timestamp.sec gt 1735019278 |
HTTP version values: HTTP/1.0, HTTP/1.1, HTTP/2.0, HTTP/3.0
Map Fields (support key access with ["key"])
| Field | Type | Description | Access example |
|---|---|---|---|
http.request.headers | Object | Request headers | http.request.headers["accept"] |
http.request.cookies | Map | Individual cookies | http.request.cookies["session"] |
http.request.uri.args | Map | Query parameters | http.request.uri.args["page"] |
http.request.body.form | Map | Form body fields | http.request.body.form["username"] |
IP / Geo Fields
| Field | Type | Supported Operators | Example |
|---|---|---|---|
ip.src | IP | in, eq, ne, in_list | ip.src in {192.168.1.1 10.0.0.5} |
ip.geoip.country | String | in, eq, ne | ip.geoip.country eq "CN" |
ip.geoip.continent | String | in, eq, ne | ip.geoip.continent eq "AS" |
ip.geoip.asnum | Integer | in, eq, ne, in_list | ip.geoip.asnum eq 45104 |
ip.src.isp | String | in, eq, ne | ip.src.isp contains "China Telecom" |
ip.src.version | String | eq, ne | ip.src.version eq "IPv4" |
ip.src.subdivision_1_iso_code | String | in, eq, ne | ip.src.subdivision_1_iso_code in {"CN-ZJ" "CN-GD"} |
ip.src.region_code | String | in, eq, ne | ip.src.region_code eq "us-west-1" |
Note: ip.geoip.asnum is an integer, do not use quotes.
Boolean Fields
| Field | Type | Description |
|---|---|---|
ssl | Boolean | Whether the request is HTTPS |
Extended Fields (advanced, may require specific plan)
| Field | Type | Description |
|---|---|---|
ali.ja3_hash | String | JA3 TLS fingerprint hash |
ali.ja4 | String | JA4 TLS fingerprint |
ali.js_detection.passed | Boolean | Whether JS challenge passed |
ali.static_resource | Boolean | Whether request is for a static resource |
ali.tls_client_auth.cert_verified | Boolean | Whether client TLS cert is verified |
ali.tls_hash | String | TLS hash |
Field selection guidance
| Scenario | Recommended field |
|---|---|
| Match by domain | http.host |
| Match URL path or suffix | http.request.uri |
| Match file type | http.request.uri.path.extension |
| Match specific file | http.request.uri.path.full_file_name |
| Match query parameter | http.request.uri.args["key"] |
| Match by country | ip.geoip.country |
| Match by IP | ip.src |
| Match by request method | http.request.method |
| Match by header | http.request.headers["header-name"] |
| Match by cookie | http.request.cookies["cookie-name"] |
Notes
- Not all fields are available for all rule types. If a field causes
CompileRuleError, try a related field (e.g. usehttp.request.uriinstead ofhttp.request.uri.path). - Map fields accessed with
["key"]return the value for that key as a string. - Extended fields (
ali.*) may require specific plan levels or feature activation.
ESA Rule Expression - Operators
Complete list of operators available in ESA rule expressions.
Forbidden Operator Formats (CRITICAL)
These formats are ABSOLUTELY FORBIDDEN and will cause errors:
| Forbidden Format | Why | Correct Alternative |
|---|---|---|
not...eq | Invalid syntax | Use ne |
not...ne | Invalid syntax | Use eq |
not in | Wrong position | Use not...in (not before field) |
not contains | Wrong position | Use not...contains (not before field) |
not matches | Wrong position | Use not...matches (not before field) |
field ends_with "value" | Wrong syntax | Use ends_with(field, "value") |
field starts_with "value" | Wrong syntax | Use starts_with(field, "value") |
Correct negation patterns:
# Negating equality
WRONG: not http.host eq "a.com"
RIGHT: http.host ne "a.com"
# Negating set membership
WRONG: http.host not in {"a.com" "b.com"}
RIGHT: not http.host in {"a.com" "b.com"}
# Negating contains
WRONG: http.request.uri not contains "/test"
RIGHT: not http.request.uri contains "/test"String Comparison (Infix Style)
Syntax: (field operator "value")
| Operator | Description | Syntax | Example |
|---|---|---|---|
eq | Equal to | (field eq "value") | (http.host eq "www.example.com") |
ne | Not equal to | (field ne "value") | (http.host ne "test.example.com") |
contains | Contains substring | (field contains "value") | (http.request.uri contains "/test") |
in | In a set of values | (field in {"v1" "v2"}) | (http.host in {"a.com" "b.com"}) |
matches | Regex match (PCRE) | (field matches "regex") | (http.request.uri matches "^/api/v[0-9]+") |
Important notes:
inuses space-separated values in braces, NOT comma-separated:{"a" "b" "c"}matchesrequires standard plan or above; basic plan returnsRuleRegexQuotaCheckFailedcontainsworks withhttp.request.uriandhttp.host. When matching URI, include the path separator (e.g."/test"not"test")
String Comparison (Function Style)
Syntax: (function(field, "value"))
| Operator | Description | Syntax | Example |
|---|---|---|---|
starts_with | Starts with prefix | (starts_with(field, "value")) | (starts_with(http.request.uri, "/api/")) |
ends_with | Ends with suffix | (ends_with(field, "value")) | (ends_with(http.request.uri, ".html")) |
Critical: starts_with and ends_with MUST use function-call syntax. Infix syntax like (field ends_with "value") will cause CompileRuleError.
Numeric Comparison (Infix Style)
Syntax: (field operator number)
| Operator | Description | Syntax | Example |
|---|---|---|---|
eq | Equal to | (field eq num) | (ip.geoip.asnum eq 45104) |
ne | Not equal to | (field ne num) | (ip.geoip.asnum ne 45104) |
lt | Less than | (field lt num) | (ip.geoip.asnum lt 45104) |
le | Less than or equal | (field le num) | (ip.geoip.asnum le 45104) |
gt | Greater than | (field gt num) | (ip.geoip.asnum gt 45104) |
ge | Greater than or equal | (field ge num) | (ip.geoip.asnum ge 45104) |
Length Check (Function Style)
Syntax: (len(field) operator number)
| Operator | Description | Syntax | Example |
|---|---|---|---|
len eq | Length equals | (len(field) eq num) | (len(http.cookie) eq 100) |
len gt | Length greater than | (len(field) gt num) | (len(http.cookie) gt 1024) |
len lt | Length less than | (len(field) lt num) | (len(http.request.uri) lt 2048) |
Existence Check (Function Style)
| Operator | Description | Syntax | Example |
|---|---|---|---|
exists | Field exists | (exists(field)) | (exists(http.request.headers["authorization"])) |
not exists | Field not exists | (not exists(field)) | (not exists(http.request.headers["authorization"])) |
Transformation (Function Style)
| Function | Description | Syntax | Example |
|---|---|---|---|
lower | Convert to lowercase | lower(field) | (lower(http.request.uri) contains "api") |
Use lower() for case-insensitive matching by wrapping the field.
Negation
Any expression can be negated with not:
| Operator | Syntax | Example |
|---|---|---|
not | (not expr) | (not http.host contains "test") |
not...contains | (not field contains "value") | (not http.host contains "staging") |
not...in | (not field in {...}) | (not http.host in {"a.com" "b.com"}) |
not...matches | (not field matches "regex") | (not http.request.uri matches "^/internal") |
not starts_with | (not starts_with(field, "v")) | (not starts_with(http.request.uri, "/admin")) |
not ends_with | (not ends_with(field, "v")) | (not ends_with(http.request.uri, ".json")) |
Logical Operators
| Operator | Description | Syntax | Example |
|---|---|---|---|
and | Both conditions | (expr1 and expr2) | (http.host eq "a.com" and starts_with(http.request.uri, "/api")) |
or | Either condition | (expr1) or (expr2) | (http.host eq "a.com") or (http.host eq "b.com") |
Nesting rules:
- Max nesting depth: 2 levels
andconditions go inside the same parentheses:(A and B and C)orconditions separate parenthesized groups:(A) or (B) or (C)- Mixed:
(A and B) or (C and D)
Plan Limitations
| Plan | eq/ne/in/starts_with/ends_with | contains | matches (regex) |
|---|---|---|---|
| Basic | Supported | Supported | Not supported |
| Standard | Supported | Supported | Supported |
| Enterprise | Supported | Supported | Supported |
Common CompileRuleError causes
1. Using ends_with/starts_with as infix operators instead of function syntax 2. Using wildcard (not a valid ESA operator) 3. Using contains with URI but missing path separator (use "/test" not "test") 4. Using unsupported match fields for the specific rule type 5. Missing parentheses around the expression 6. Using comma-separated values in in instead of space-separated
ESA Sites
Sites are the basic management unit of ESA. Each site corresponds to a domain (or subdomain).
Common operations
- Create:
CreateSite - List:
ListSites(supports pagination and filters) - Query:
GetSite - Delete:
DeleteSite - Check name:
CheckSiteName - Change access type:
UpdateSiteAccessType - Change coverage:
UpdateSiteCoverage
Access types
- CNAME: Keep original DNS, add CNAME record pointing to ESA edge node.
- Pros: Simple setup, no DNS migration needed.
- Limitation: DNS record APIs are NOT available (CreateRecord, ListRecords, etc. will fail).
- NS: Delegate nameservers to ESA, ESA takes over DNS resolution.
- Pros: Full DNS management via API.
- Limitation: Must change NS records at domain registrar.
Switching access type
Use UpdateSiteAccessType to switch between CNAME and NS.
- NS -> CNAME: Will fail if incompatible DNS records exist. Delete A/AAAA records first.
- CNAME -> NS: Works directly. After switching, manage DNS via ESA API.
Coverage options
domestic: China mainland onlyoverseas: Outside China mainlandglobal: Worldwide
Plan types
basicplan/standardplan/advancedplan/enterpriseplan
ListSites filters
SiteName: Fuzzy matchStatus: Site status (active, pending, etc.)AccessType: NS or CNAMECoverage: domestic, overseas, globalPlanSubscribeType: Plan type
Behavioral notes
- Newly created sites start as
pending; complete access verification to activate. - Deleting a site removes all associated configuration (DNS records, cache rules, WAF rules, certificates, etc.).
ListSitesreturns sites across all regions; no need to iterate regions.
References
- CreateSite: https://help.aliyun.com/zh/esa/developer-reference/api-esa-2024-09-10-createsite
- ListSites: https://help.aliyun.com/zh/esa/developer-reference/api-esa-2024-09-10-listsites
- Access types: https://help.aliyun.com/zh/esa/user-guide/add-site
Official documentation sources (for future updates) ====================================================
Product pages
- OpenAPI product page: https://api.aliyun.com/product/ESA
- API metadata: https://api.aliyun.com/meta/v1/products/ESA/versions/2024-09-10/api-docs.json
- API overview: https://www.alibabacloud.com/help/en/esa/developer-reference/api-esa-2024-09-10-overview
- Endpoints: https://www.alibabacloud.com/help/en/esa/developer-reference/endpoints
- Product home: https://www.alibabacloud.com/help/en/esa/
- Quick start: https://www.alibabacloud.com/help/en/esa/getting-started/
Key operation docs
- ListSites: https://help.aliyun.com/zh/esa/developer-reference/api-esa-2024-09-10-listsites
- CreateSite: https://help.aliyun.com/zh/esa/developer-reference/api-esa-2024-09-10-createsite
- CreateRecord: https://help.aliyun.com/zh/esa/developer-reference/api-esa-2024-09-10-createrecord
- ListRecords: https://help.aliyun.com/zh/esa/developer-reference/api-esa-2024-09-10-listrecords
- PurgeCaches: https://help.aliyun.com/zh/esa/developer-reference/api-esa-2024-09-10-purgecaches
- PreloadCaches: https://help.aliyun.com/zh/esa/developer-reference/api-esa-2024-09-10-preloadcaches
- ApplyCertificate: https://help.aliyun.com/zh/esa/developer-reference/api-esa-2024-09-10-applycertificate
- CreateWafRule: https://help.aliyun.com/zh/esa/developer-reference/api-esa-2024-09-10-createwafrule
- ListWafRulesets: https://help.aliyun.com/zh/esa/developer-reference/api-esa-2024-09-10-listwafrulesets
- CreateOriginPool: https://help.aliyun.com/zh/esa/developer-reference/api-esa-2024-09-10-createoriginpool
- CreateCacheRule: https://help.aliyun.com/zh/esa/developer-reference/api-esa-2024-09-10-createcacherule
Access type docs
- CNAME vs NS: https://help.aliyun.com/zh/esa/user-guide/add-site
DescribeSiteTimeSeriesData API Reference
Query account-level or site-level traffic analysis time-series data.
Official docs: https://api.aliyun.com/document/ESA/2024-09-10/DescribeSiteTimeSeriesData
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
SiteId | string | No | Site ID. Empty = account-level query |
StartTime | string | No | Start time (ISO8601 UTC+0) |
EndTime | string | No | End time (ISO8601 UTC+0) |
Interval | string | No | Time granularity in seconds |
Fields | array | Yes | Query metrics array |
Fields Structure
| Parameter | Type | Required | Description |
|---|---|---|---|
FieldName | string | No | Metric name (e.g., Traffic, Request) |
Dimension | array | No | Dimension array (e.g., ["SiteId"]) |
Response Parameters
| Parameter | Type | Description |
|---|---|---|
Data | array | Time-series data array |
SummarizedData | array | Aggregated summary data |
StartTime | string | Query start time |
EndTime | string | Query end time |
Interval | long | Data granularity (seconds) |
SamplingRate | float | Sampling rate (%) |
RequestId | string | Request ID |
Data Structure
| Parameter | Type | Description |
|---|---|---|
FieldName | string | Metric name |
DimensionName | string | Dimension name |
DimensionValue | string | Dimension value |
DimensionValueAlias | string | Dimension alias (e.g., site name) |
DetailData | array | Time-series points |
DetailData Structure
| Parameter | Type | Description |
|---|---|---|
TimeStamp | string | Time point (ISO8601) |
Value | any | Metric value |
SummarizedData Structure
| Parameter | Type | Description |
|---|---|---|
FieldName | string | Metric name |
DimensionName | string | Dimension name |
DimensionValue | string | Dimension value |
AggMethod | string | Aggregation method (sum, avg) |
Value | any | Aggregated value |
Time Granularity Rules
| Time Range | Interval | Parameter Value |
|---|---|---|
| <= 3 hours | 1 minute | 60 |
| 3-12 hours | 5 minutes | 300 |
| 12 hours - 1 day | 15 minutes | 900 |
| 1-10 days | 1 hour | 3600 |
| 10-31 days | 1 day | 86400 |
Default: If StartTime and EndTime not specified, returns last 24 hours.
Note: Large time ranges may use sampling.
Code Examples
Query site traffic (hourly)
from alibabacloud_esa20240910.client import Client as EsaClient
from alibabacloud_esa20240910 import models as esa_models
from alibabacloud_tea_openapi import models as open_api_models
from datetime import datetime, timedelta, timezone
def create_client():
config = open_api_models.Config(
region_id="cn-hangzhou",
endpoint="esa.cn-hangzhou.aliyuncs.com",
)
return EsaClient(config)
def query_traffic(site_id: str, hours: int = 24):
client = create_client()
end_time = datetime.now(timezone.utc)
start_time = end_time - timedelta(hours=hours)
resp = client.describe_site_time_series_data(
esa_models.DescribeSiteTimeSeriesDataRequest(
site_id=site_id,
start_time=start_time.strftime("%Y-%m-%dT%H:%M:%SZ"),
end_time=end_time.strftime("%Y-%m-%dT%H:%M:%SZ"),
interval="3600", # 1 hour
fields=[
esa_models.DescribeSiteTimeSeriesDataRequestFields(
field_name="Traffic",
dimension=["SiteId"]
)
]
)
)
return resp.body
# Usage
result = query_traffic("974351557069296", hours=24)
for item in result.data:
print(f"Site: {item.dimension_value_alias}")
for point in item.detail_data:
print(f" {point.time_stamp}: {point.value} bytes")Query multiple metrics
def query_multiple_metrics(site_id: str):
client = create_client()
resp = client.describe_site_time_series_data(
esa_models.DescribeSiteTimeSeriesDataRequest(
site_id=site_id,
start_time="2026-03-09T00:00:00Z",
end_time="2026-03-10T00:00:00Z",
interval="3600",
fields=[
esa_models.DescribeSiteTimeSeriesDataRequestFields(
field_name="Traffic",
dimension=["ALL"]
),
esa_models.DescribeSiteTimeSeriesDataRequestFields(
field_name="Request",
dimension=["ALL"]
),
esa_models.DescribeSiteTimeSeriesDataRequestFields(
field_name="HitRate",
dimension=["ALL"]
)
]
)
)
return resp.bodyQuery by country
def query_traffic_by_country(site_id: str):
client = create_client()
resp = client.describe_site_time_series_data(
esa_models.DescribeSiteTimeSeriesDataRequest(
site_id=site_id,
start_time="2026-03-09T00:00:00Z",
end_time="2026-03-10T00:00:00Z",
interval="3600",
fields=[
esa_models.DescribeSiteTimeSeriesDataRequestFields(
field_name="Traffic",
dimension=["ClientCountryCode"]
)
]
)
)
# Group by country
for item in resp.body.data:
country = item.dimension_value
print(f"Country: {country}")
for point in item.detail_data:
print(f" {point.time_stamp}: {point.value}")Response Example
{
"Data": [
{
"FieldName": "Traffic",
"DimensionName": "SiteId",
"DimensionValue": "974351557069296",
"DimensionValueAlias": "lwcwiki.fun",
"DetailData": [
{"TimeStamp": "2026-03-09T16:00:00Z", "Value": 38428},
{"TimeStamp": "2026-03-09T17:00:00Z", "Value": 1042},
{"TimeStamp": "2026-03-09T18:00:00Z", "Value": 629}
]
}
],
"SummarizedData": [
{
"FieldName": "Traffic",
"DimensionName": "SiteId",
"DimensionValue": "974351557069296",
"DimensionValueAlias": "lwcwiki.fun",
"AggMethod": "sum",
"Value": 27317844
}
],
"StartTime": "2026-03-09T16:00:00Z",
"EndTime": "2026-03-10T15:59:00Z",
"Interval": 3600,
"SamplingRate": 100
}Error Codes
| HTTP Code | Error Code | Description |
|---|---|---|
| 400 | InvalidParameter.TimeRange | Time range exceeds limit |
| 400 | InvalidEndTime.Mismatch | EndTime earlier than StartTime |
| 400 | InvalidParameter.Field | Invalid field name |
| 400 | InvalidParameter.Dimension | Invalid dimension |
| 400 | InvalidTime.Malformed | Wrong time format |
| 400 | TooManyDimensions | Too many query dimensions |
| 400 | TooManyRequests | Rate limited |
DescribeSiteTopData API Reference
Query account-level or site-level traffic analysis Top-N ranking data.
Official docs: https://api.aliyun.com/document/ESA/2024-09-10/DescribeSiteTopData
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
SiteId | string | No | Site ID. Empty = account-level query |
StartTime | string | No | Start time (ISO8601 UTC+0) |
EndTime | string | No | End time (ISO8601 UTC+0) |
Interval | string | No | Time granularity in seconds |
Fields | array | Yes | Query metrics array |
Limit | string | No | Top-N count. Values: 5, 10, 150 |
Fields Structure
| Parameter | Type | Required | Description |
|---|---|---|---|
FieldName | string | No | Metric name (e.g., Traffic, Request) |
Dimension | array | No | Dimension array (e.g., ["ClientCountryCode"]) |
Response Parameters
| Parameter | Type | Description |
|---|---|---|
Data | array | Top-N data array |
StartTime | string | Query start time |
EndTime | string | Query end time |
SamplingRate | float | Sampling rate (%) |
RequestId | string | Request ID |
Data Structure
| Parameter | Type | Description |
|---|---|---|
FieldName | string | Metric name |
DimensionName | string | Dimension name |
DetailData | array | Top-N ranking data |
DetailData Structure
| Parameter | Type | Description |
|---|---|---|
DimensionValue | string | Dimension value (e.g., country code) |
Value | any | Metric value |
Code Examples
Query top 10 countries by traffic
from alibabacloud_esa20240910.client import Client as EsaClient
from alibabacloud_esa20240910 import models as esa_models
from alibabacloud_tea_openapi import models as open_api_models
from datetime import datetime, timedelta, timezone
def create_client():
config = open_api_models.Config(
region_id="cn-hangzhou",
endpoint="esa.cn-hangzhou.aliyuncs.com",
)
return EsaClient(config)
def query_top_countries(site_id: str, limit: int = 10):
client = create_client()
end_time = datetime.now(timezone.utc)
start_time = end_time - timedelta(hours=24)
resp = client.describe_site_top_data(
esa_models.DescribeSiteTopDataRequest(
site_id=site_id,
start_time=start_time.strftime("%Y-%m-%dT%H:%M:%SZ"),
end_time=end_time.strftime("%Y-%m-%dT%H:%M:%SZ"),
fields=[
esa_models.DescribeSiteTopDataRequestFields(
field_name="Traffic",
dimension=["ClientCountryCode"]
)
],
limit=str(limit)
)
)
return resp.body
# Usage
result = query_top_countries("974351557069296", limit=10)
for item in result.data:
print(f"Metric: {item.field_name} by {item.dimension_name}")
for i, rank in enumerate(item.detail_data, 1):
print(f" #{i} {rank.dimension_value}: {rank.value} bytes")Query top hosts by requests
def query_top_hosts(site_id: str):
client = create_client()
resp = client.describe_site_top_data(
esa_models.DescribeSiteTopDataRequest(
site_id=site_id,
start_time="2026-03-10T00:00:00Z",
end_time="2026-03-11T00:00:00Z",
fields=[
esa_models.DescribeSiteTopDataRequestFields(
field_name="Request",
dimension=["Host"]
)
],
limit="10"
)
)
return resp.bodyQuery top URLs by traffic
def query_top_urls(site_id: str):
client = create_client()
resp = client.describe_site_top_data(
esa_models.DescribeSiteTopDataRequest(
site_id=site_id,
start_time="2026-03-10T00:00:00Z",
end_time="2026-03-11T00:00:00Z",
fields=[
esa_models.DescribeSiteTopDataRequestFields(
field_name="Traffic",
dimension=["RequestUri"]
)
],
limit="150"
)
)
return resp.bodyQuery account-level top sites
def query_top_sites():
"""Query top sites at account level (no SiteId)"""
client = create_client()
resp = client.describe_site_top_data(
esa_models.DescribeSiteTopDataRequest(
# No site_id = account level
start_time="2026-03-10T00:00:00Z",
end_time="2026-03-11T00:00:00Z",
fields=[
esa_models.DescribeSiteTopDataRequestFields(
field_name="Traffic",
dimension=["SiteId"]
)
],
limit="10"
)
)
return resp.bodyResponse Example
{
"Data": [
{
"FieldName": "Traffic",
"DimensionName": "ClientCountryCode",
"DetailData": [
{"DimensionValue": "HK", "Value": 9132354},
{"DimensionValue": "CN", "Value": 703820},
{"DimensionValue": "CA", "Value": 64679},
{"DimensionValue": "US", "Value": 33979},
{"DimensionValue": "SG", "Value": 27666}
]
}
],
"StartTime": "2026-03-10T02:47:00Z",
"EndTime": "2026-03-11T02:47:00Z",
"SamplingRate": 100
}Common Use Cases
| Use Case | FieldName | Dimension |
|---|---|---|
| Top countries by traffic | Traffic | ClientCountryCode |
| Top provinces (China) | Traffic | ClientProvinceCode |
| Top ISPs | Traffic | ClientISP |
| Top hosts | Request | Host |
| Top URLs | Traffic | RequestUri |
| Top sites (account level) | Traffic | SiteId |
| Top status codes | Request | Status |
Error Codes
| HTTP Code | Error Code | Description |
|---|---|---|
| 400 | InvalidParameter.TimeRange | Time range exceeds limit |
| 400 | InvalidEndTime.Mismatch | EndTime earlier than StartTime |
| 400 | InvalidParameter.Field | Invalid field name |
| 400 | InvalidParameter.Dimension | Invalid dimension |
| 400 | InvalidTime.Malformed | Wrong time format |
| 400 | TooManyDimensions | Too many query dimensions |
| 400 | TooManyRequests | Rate limited |
Differences from Time-Series API
| Feature | Time-Series | Top-N |
|---|---|---|
| Purpose | Trend over time | Ranking at a point |
| Returns | Time-stamped values | Ranked values |
| Limit | N/A | 5, 10, or 150 |
| SummarizedData | Yes | No |
#!/usr/bin/env python3
"""Check ESA site status and configuration overview.
Displays site info and configuration settings (IPv6, dev mode, tiered cache, etc.).
"""
from __future__ import annotations
import argparse
import os
from alibabacloud_esa20240910.client import Client as Esa20240910Client
from alibabacloud_esa20240910 import models as esa_models
from alibabacloud_tea_openapi import models as open_api_models
def create_client() -> Esa20240910Client:
config = open_api_models.Config(
region_id="cn-hangzhou",
endpoint="esa.cn-hangzhou.aliyuncs.com",
)
ak = os.getenv("ALIBABACLOUD_ACCESS_KEY_ID") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID") or os.getenv("ALICLOUD_ACCESS_KEY_ID")
sk = os.getenv("ALIBABACLOUD_ACCESS_KEY_SECRET") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET") or os.getenv("ALICLOUD_ACCESS_KEY_SECRET")
token = os.getenv("ALIBABACLOUD_SECURITY_TOKEN") or os.getenv("ALIBABA_CLOUD_SECURITY_TOKEN") or os.getenv("ALICLOUD_SECURITY_TOKEN")
if ak and sk:
config.access_key_id = ak
config.access_key_secret = sk
if token:
config.security_token = token
return Esa20240910Client(config)
def main() -> int:
parser = argparse.ArgumentParser(description="Check ESA site status and configuration")
parser.add_argument("--site-id", type=int, required=True, help="ESA site ID")
args = parser.parse_args()
client = create_client()
site_id = args.site_id
# 1. Site info
site_resp = client.get_site(esa_models.GetSiteRequest(site_id=site_id))
site = site_resp.body.site_model # GetSite returns nested site_model object
print("=== Site Info ===")
print(f" Site Name: {getattr(site, 'site_name', 'N/A')}")
print(f" Site ID: {site_id}")
print(f" Status: {getattr(site, 'status', 'N/A')}")
print(f" Access Type: {getattr(site, 'access_type', 'N/A')}")
print(f" Plan: {getattr(site, 'plan_name', 'N/A')}")
print(f" Coverage: {getattr(site, 'coverage', 'N/A')}")
print(f" CNAME Zone: {getattr(site, 'cname_zone', 'N/A')}")
print()
# 2. Tiered cache config
try:
cache_resp = client.get_tiered_cache(esa_models.GetTieredCacheRequest(site_id=site_id))
print("=== Tiered Cache ===")
print(f" Architecture: {getattr(cache_resp.body, 'cache_architecture_mode', 'N/A')}")
except Exception as e:
print(f"=== Tiered Cache ===\n Error: {e}")
print()
# 3. IPv6 config
try:
ipv6_resp = client.get_ipv6(esa_models.GetIPv6Request(site_id=site_id))
print("=== IPv6 ===")
print(f" Enabled: {getattr(ipv6_resp.body, 'enable', 'N/A')}")
except Exception as e:
print(f"=== IPv6 ===\n Error: {e}")
print()
# 4. Development mode
try:
dev_resp = client.get_development_mode(esa_models.GetDevelopmentModeRequest(site_id=site_id))
print("=== Development Mode ===")
print(f" Enabled: {getattr(dev_resp.body, 'enable', 'N/A')}")
except Exception as e:
print(f"=== Development Mode ===\n Error: {e}")
print()
# 5. SEO bypass
try:
seo_resp = client.get_seo_bypass(esa_models.GetSeoBypassRequest(site_id=site_id))
print("=== SEO Bypass ===")
print(f" Enabled: {getattr(seo_resp.body, 'enable', 'N/A')}")
except Exception as e:
print(f"=== SEO Bypass ===\n Error: {e}")
return 0
if __name__ == "__main__":
raise SystemExit(main())
#!/usr/bin/env python3
"""List DNS records for an ESA site.
Only works for NS-connected sites. CNAME sites will return an error.
"""
from __future__ import annotations
import argparse
import json
import os
import sys
from alibabacloud_esa20240910.client import Client as Esa20240910Client
from alibabacloud_esa20240910 import models as esa_models
from alibabacloud_tea_openapi import models as open_api_models
def create_client() -> Esa20240910Client:
config = open_api_models.Config(
region_id="cn-hangzhou",
endpoint="esa.cn-hangzhou.aliyuncs.com",
)
ak = os.getenv("ALIBABACLOUD_ACCESS_KEY_ID") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID") or os.getenv("ALICLOUD_ACCESS_KEY_ID")
sk = os.getenv("ALIBABACLOUD_ACCESS_KEY_SECRET") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET") or os.getenv("ALICLOUD_ACCESS_KEY_SECRET")
token = os.getenv("ALIBABACLOUD_SECURITY_TOKEN") or os.getenv("ALIBABA_CLOUD_SECURITY_TOKEN") or os.getenv("ALICLOUD_SECURITY_TOKEN")
if ak and sk:
config.access_key_id = ak
config.access_key_secret = sk
if token:
config.security_token = token
return Esa20240910Client(config)
def iter_records(client: Esa20240910Client, site_id: int, record_type: str | None = None):
"""Iterate over all DNS records for a site with pagination."""
page_number = 1
page_size = 100
while True:
req = esa_models.ListRecordsRequest(
site_id=site_id,
page_number=page_number,
page_size=page_size,
)
if record_type:
req.type = record_type
resp = client.list_records(req)
records = resp.body.records or []
for rec in records:
yield rec
total = resp.body.total_count or 0
if page_number * page_size >= total:
break
page_number += 1
def to_record(rec) -> dict:
"""Convert a record object to a dict."""
return {
"record_id": rec.record_id,
"record_name": rec.record_name,
"type": rec.type,
"data": getattr(rec, "data", None) or getattr(rec, "record_data", None),
"ttl": rec.ttl,
"proxied": rec.proxied,
"create_time": rec.create_time,
"update_time": rec.update_time,
}
def main() -> int:
parser = argparse.ArgumentParser(description="List DNS records for an ESA site")
parser.add_argument("--site-id", type=int, required=True, help="ESA site ID")
parser.add_argument("--type", dest="record_type", help="Filter by record type (e.g., A/AAAA, CNAME)")
parser.add_argument("--json", action="store_true", help="Output as JSON")
args = parser.parse_args()
client = create_client()
try:
records = [to_record(rec) for rec in iter_records(client, args.site_id, args.record_type)]
except Exception as e:
if "CnameSiteRecordUnsupport" in str(e):
print("Error: DNS record APIs are not available for CNAME-connected sites.", file=sys.stderr)
print("This site uses CNAME access type. Switch to NS access to manage DNS records.", file=sys.stderr)
return 1
raise
if args.json:
print(json.dumps({"records": records, "total": len(records)}, indent=2, ensure_ascii=False))
else:
if not records:
print("No DNS records found.")
return 0
# TSV output
print("record_name\ttype\tdata\tttl\tproxied")
for rec in records:
print(f"{rec['record_name']}\t{rec['type']}\t{rec['data']}\t{rec['ttl']}\t{rec['proxied']}")
return 0
if __name__ == "__main__":
raise SystemExit(main())
#!/usr/bin/env python3
"""List all ESA sites.
Outputs TSV by default. Use --json for JSON output.
"""
from __future__ import annotations
import argparse
import json
import os
from typing import Iterable
from alibabacloud_esa20240910.client import Client as Esa20240910Client
from alibabacloud_esa20240910 import models as esa_models
from alibabacloud_tea_openapi import models as open_api_models
def create_client() -> Esa20240910Client:
config = open_api_models.Config(
region_id="cn-hangzhou",
endpoint="esa.cn-hangzhou.aliyuncs.com",
)
ak = os.getenv("ALIBABACLOUD_ACCESS_KEY_ID") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID") or os.getenv("ALICLOUD_ACCESS_KEY_ID")
sk = os.getenv("ALIBABACLOUD_ACCESS_KEY_SECRET") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET") or os.getenv("ALICLOUD_ACCESS_KEY_SECRET")
token = os.getenv("ALIBABACLOUD_SECURITY_TOKEN") or os.getenv("ALIBABA_CLOUD_SECURITY_TOKEN") or os.getenv("ALICLOUD_SECURITY_TOKEN")
if ak and sk:
config.access_key_id = ak
config.access_key_secret = sk
if token:
config.security_token = token
return Esa20240910Client(config)
def iter_sites(client: Esa20240910Client) -> Iterable:
page_number = 1
page_size = 50
while True:
resp = client.list_sites(esa_models.ListSitesRequest(
page_number=page_number,
page_size=page_size,
))
for site in resp.body.sites:
yield site
total = resp.body.total_count
if page_number * page_size >= total:
break
page_number += 1
def to_record(site) -> dict:
return {
"site_id": site.site_id,
"site_name": site.site_name,
"status": site.status,
"access_type": site.access_type,
"plan_name": site.plan_name,
"coverage": site.coverage,
"cname_zone": getattr(site, "cname_zone", None),
"create_time": site.create_time,
}
def main() -> int:
parser = argparse.ArgumentParser(description="List all ESA sites")
parser.add_argument("--json", action="store_true", help="Output JSON array")
parser.add_argument("--output", help="Write output to file")
args = parser.parse_args()
client = create_client()
records = [to_record(site) for site in iter_sites(client)]
if args.json:
output = json.dumps(records, ensure_ascii=False, indent=2)
else:
lines = [
"site_id\tsite_name\tstatus\taccess_type\tplan_name\tcoverage\tcname_zone\tcreate_time"
]
for r in records:
lines.append(
"\t".join(
str(r.get(k) or "")
for k in [
"site_id", "site_name", "status", "access_type",
"plan_name", "coverage", "cname_zone", "create_time",
]
)
)
output = "\n".join(lines)
if args.output:
with open(args.output, "w", encoding="utf-8") as f:
f.write(output)
else:
print(output)
return 0
if __name__ == "__main__":
raise SystemExit(main())
#!/usr/bin/env python3
"""Summarize ESA sites by plan type.
Outputs TSV by default. Use --json for JSON output.
"""
from __future__ import annotations
import argparse
import json
import os
from collections import Counter
from alibabacloud_esa20240910.client import Client as Esa20240910Client
from alibabacloud_esa20240910 import models as esa_models
from alibabacloud_tea_openapi import models as open_api_models
def create_client() -> Esa20240910Client:
config = open_api_models.Config(
region_id="cn-hangzhou",
endpoint="esa.cn-hangzhou.aliyuncs.com",
)
ak = os.getenv("ALIBABACLOUD_ACCESS_KEY_ID") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID") or os.getenv("ALICLOUD_ACCESS_KEY_ID")
sk = os.getenv("ALIBABACLOUD_ACCESS_KEY_SECRET") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET") or os.getenv("ALICLOUD_ACCESS_KEY_SECRET")
token = os.getenv("ALIBABACLOUD_SECURITY_TOKEN") or os.getenv("ALIBABA_CLOUD_SECURITY_TOKEN") or os.getenv("ALICLOUD_SECURITY_TOKEN")
if ak and sk:
config.access_key_id = ak
config.access_key_secret = sk
if token:
config.security_token = token
return Esa20240910Client(config)
def iter_sites(client: Esa20240910Client):
page_number = 1
page_size = 50
while True:
resp = client.list_sites(esa_models.ListSitesRequest(
page_number=page_number,
page_size=page_size,
))
for site in resp.body.sites:
yield site
total = resp.body.total_count
if page_number * page_size >= total:
break
page_number += 1
def main() -> int:
parser = argparse.ArgumentParser(description="Summarize ESA sites by plan")
parser.add_argument("--by-status", action="store_true", help="Further break down by status")
parser.add_argument("--json", action="store_true", help="Output JSON")
parser.add_argument("--output", help="Write output to file")
args = parser.parse_args()
client = create_client()
if args.by_status:
counter: Counter = Counter()
for site in iter_sites(client):
key = (site.plan_name or "unknown", site.status or "unknown")
counter[key] += 1
records = [
{"plan_name": k[0], "status": k[1], "count": v}
for k, v in sorted(counter.items())
]
header = "plan_name\tstatus\tcount"
keys = ["plan_name", "status", "count"]
else:
counter = Counter()
for site in iter_sites(client):
counter[site.plan_name or "unknown"] += 1
records = [
{"plan_name": k, "count": v}
for k, v in sorted(counter.items())
]
header = "plan_name\tcount"
keys = ["plan_name", "count"]
if args.json:
output = json.dumps(records, ensure_ascii=False, indent=2)
else:
lines = [header]
for r in records:
lines.append("\t".join(str(r.get(k) or "") for k in keys))
output = "\n".join(lines)
if args.output:
with open(args.output, "w", encoding="utf-8") as f:
f.write(output)
else:
print(output)
return 0
if __name__ == "__main__":
raise SystemExit(main())