EdgeScript lets you write custom logic that runs on Alibaba Cloud CDN points of presence (POPs) — without touching your origin server. Use EdgeScript to control authentication, headers, URL rewrites, cache time-to-live (TTL) values, throttling, and access control at the edge.
The following table summarizes the scenarios covered in this document:
| Scenario | What it does |
|---|---|
| Authentication | Validates URL signatures for .ts requests; returns HTTP 403 on failure |
| Request and response headers | Sets Content-Disposition to trigger a named file download |
| Rewrites and redirects | Rewrites URIs, changes file extensions, normalizes case, adds prefixes, performs 302 redirects |
| Cache control | Sets TTL values per URL pattern or HTTP status code |
| Throttling | Limits transfer rate based on URL parameters |
| Region and ISP access control | Blocks requests from specific regions or Internet service providers (ISPs) |
Customize authentication rules
EdgeScript authentication can cover a range of scenarios, including URL signature validation (hotlink protection), IP allowlist enforcement, and User-Agent filtering. The script below implements URL signature–based hotlink protection for .ts requests.
The script enforces three rules in sequence:
The request must include both the
t(expiration time) andkeyparameters — otherwise the POP returns HTTP 403.The
tparameter must be a valid number and must not be earlier than the current POP time — otherwise the POP returns HTTP 403.The MD5 hash of
(private key + path + filename.extension)must match thedigestsegment in the URL — otherwise the POP returns HTTP 403.
Request URL format: /path/digest/?.ts?key=&t=
# Only apply authentication to .ts requests
if eq(substr($uri, -3, -1), '.ts') {
# Rule 1: Reject if t or key parameters are missing
if or(not($arg_t), not($arg_key)) {
add_rsp_header('X-AUTH-MSG', 'auth failed - missing necessary arg')
exit(403)
}
# Rule 2: Reject if t is not a valid number
t = tonumber($arg_t)
if not(t) {
add_rsp_header('X-AUTH-MSG', 'auth failed - invalid time')
exit(403)
}
# Reject if the URL has expired.
# t is compared against the POP's local clock, not the client clock.
# If the client clock differs significantly from the POP clock, valid
# requests may be rejected. Add a time buffer when generating signed URLs.
if gt(now(), t) {
add_rsp_header('X-AUTH-MSG', 'auth failed - expired url')
exit(403)
}
# Rule 3: Extract path segments using a regular expression
pcs = capture_re($request_uri,'^/([^/]+)/([^/]+)/([^?]+)%?(.*)')
sec1 = get(pcs, 1)
sec2 = get(pcs, 2)
sec3 = get(pcs, 3)
if or(not(sec1), not(sec2), not(sec3)) {
add_rsp_header('X-AUTH-MSG', 'auth failed - malformed url')
exit(403)
}
# Compute MD5(private_key + path + filename) and compare with digest
key = 'b98d643a-9170-4937-8524-6c33514bbc23'
signstr = concat(key, sec1, sec3)
digest = md5(signstr)
if ne(digest, sec2) {
add_rsp_header('X-AUTH-DEBUG', concat('signstr: ', signstr))
add_rsp_header('X-AUTH-MSG', 'auth failed - invalid digest')
exit(403)
}
}Customize request headers and response headers
The script below sets the Content-Disposition response header to trigger an automatic file download with a custom filename. When the request includes a filename URL parameter, the browser saves the response body under that name. If the parameter is absent, the browser uses the default filename.
Expected response header:
Content-Disposition: attachment;filename="monitor.apk"The filename value is wrapped in double quotation marks. tochar(34) converts ASCII code 34 to the " character, because quotation marks cannot be embedded directly in the string.
if $arg_filename {
hn = 'Content-Disposition'
hv = concat('attachment;filename=', tochar(34), $arg_filename, tochar(34))
add_rsp_header(hn, hv)
}Customize rewrites and redirects
Rewrite a URI
Rewrite /hello to /index.html. The back-to-origin request uses /index.html and all URL parameters are preserved.
if match_re($uri, '^/hello$') {
rewrite('/index.html', 'break')
}Rewrite a file extension
Replace the file extension in the URI with the value of the type URL parameter. For example, /1.txt?type=mp4 becomes /1.mp4?type=mp4 before the request is forwarded to the origin. The retrieved content is then cached on CDN POPs.
if and(match_re($uri, '^/1.txt$'), $arg_type) {
rewrite(concat('/1.', $arg_type), 'break')
}Convert a file extension to lowercase
Normalize the file extension to lowercase. For example, /image/Photo.JPG becomes /image/Photo.jpg.
pcs = capture_re($uri, '^(.+%.)([^.]+)')
section = get(pcs, 1)
postfix = get(pcs, 2)
if and(section, postfix) {
rewrite(concat(section, lower(postfix)), 'break')
}Add a URI prefix
Rewrite any URI matching ^/nn_live/(.*) by prepending /3rd. For example, /nn_live/stream1 becomes /3rd/nn_live/stream1.
pcs = capture_re($uri, '^/nn_live/(.*)')
sec = get(pcs, 1)
if sec {
dst = concat('/3rd/nn_live/', sec)
rewrite(dst, 'break')
}Perform a 302 redirect
Redirect requests to the / root path to /app/movie/pages/index/index.html.
if eq($uri, '/') {
rewrite('/app/movie/pages/index/index.html', 'redirect')
}Redirect to an HTTPS URL
Redirect requests to the root path — whether arriving over HTTP or HTTPS — to a specific HTTPS URL. Replace https://demo.aliyundoc.com/index.html with your target URL.
if eq($uri, '/') {
rewrite('https://demo.aliyundoc.com/index.html', 'redirect')
}Customize cache control
The script below sets the TTL for cached resources based on URL patterns and HTTP status codes.
# For /image URLs: cache 301 responses for 10 s, 302 responses for 5 s
if match_re($uri, '^/image') {
set_cache_ttl('code', '301=10,302=5')
}
# For .mp4 files: TTL = 5 s
if eq(substr($uri, -4, -1), '.mp4') {
set_cache_ttl('path', 5)
}
# For /201801/mp4/ paths: TTL = 50 s
if match_re($uri, '^/201801/mp4/') {
set_cache_ttl('path', 50)
}
# For /201802/flv/ paths: TTL = 10 s
if match_re($uri, '^/201802/flv/') {
set_cache_ttl('path', 10)
}set_cache_ttl accepts two modes:
'code'— sets TTL per HTTP status code, using the format'<code>=<seconds>[,<code>=<seconds>]''path'— sets TTL in seconds for all responses matching the URI pattern
Customize throttling policies
The script below reads the sp (speed limit) and unit parameters from the request URL and applies throttling using limit_rate(). Both parameters must be present for throttling to take effect.
| Parameter | Description | Valid values |
|---|---|---|
sp | Maximum transfer rate before throttling takes effect | Positive integer |
unit | Unit of the rate | k (KB) or m (MB) |
if and($arg_sp, $arg_unit) {
# Validate that sp is a positive integer
sp = tonumber($arg_sp)
if not(sp) {
add_rsp_header('X-LIMIT-DEBUG', 'invalid sp')
return false
}
# Validate that unit is k (KB) or m (MB)
if and(ne($arg_unit, 'k'), ne($arg_unit, 'm')) {
add_rsp_header('X-LIMIT-DEBUG', 'invalid unit')
return false
}
add_rsp_header('X-LIMIT-DEBUG', concat('set on: ', sp, $arg_unit))
limit_rate(sp, $arg_unit)
return true
}Region- and ISP-based access control
The script below restricts access based on the region and Internet service provider (ISP) of the client IP address. Requests from any region or ISP not in the allowlist are blocked with HTTP 403.
client_region() returns the region code of the client IP. client_isp() returns the ISP code. For the full list of region and ISP codes, see Request logic functions.
# Block requests from regions not in the allowlist
ip_region_id = client_region()
if not(match_re(ip_region_id, '440000|370000')) {
add_rsp_header('X-REGION-BLOCK-DEBUG', concat('hit ip_region_id:', ip_region_id))
exit(403)
}
# Block requests from ISPs not in the allowlist
ip_isp_id = client_isp()
if not(match_re(ip_isp_id, '100017|100025')) {
add_rsp_header('X-REGION-BLOCK-DEBUG', concat('hit ip_isp_id:', ip_isp_id))
exit(403)
}Replace '440000|370000' and '100017|100025' with the region and ISP codes that match your access policy.