Fortinet white logo
Fortinet white logo

HTTP Commands

HTTP Commands

HTTP:redirect(fmt, …)

Reply to client with redirect response.

Syntax
HTTP:redirect(fmt, …)
Arguments

Name

Description

fmt

String Type Format.

Events

Applicable in HTTP_REQUEST.

Example
when HTTP_REQUEST {
    HTTP:redirect("https://%s", HTTP:host())
}

HTTP:reply(response)

Reply to client with custom response.

Syntax
HTTP:reply(response)
Arguments

Name

Description

response

Argument response is a lua array. It includes:

  • status: Integer. Default is 200.

  • reason: String. If not set, the system will use the default value of status code. For example, if the status code is 200, the default value of reason is "OK".

  • headers: Lua table. Each value of the table is a lua array. It contains all headers except "content-length". "content-length" will be automatically set with the body size.

  • Body: String.

    To be specific:

    HTTP:reply{

    status = 400,

    reason = "test reason",

    headers = {

    ["content-type"] = { "text/html" },

    ["cache-control"] = { "no-cache", "no-store" },

    },

    body = "<html><body><h1>invalid request<h1></body></html>",

    }

Events

Applicable in HTTP_REQUEST.

Example
function reply_invalid(HTTP)
    HTTP:reply{
        status = 400,
        headers = {
            ["content-type"] = { "text/html" },
            ["cache-control"] = { "no-cache", "no-store" },
        },
        body = "<html><body><h1>Invalid API Request<h1></body></html>",
    }
end
function check_ip_reputation(HTTP)
    local v = HTTP:arg("ip")
    if v then v = ip.addr(v) end -- convert string to ip
    if not v then return reply_invalid(HTTP) end
    local r = ip.reputation(v)
    local body = string.format("<html><body><h1>Reputation of IP %s: %s<h1></body></html>",
        v, #r > 0 and table.concat(r, ', ') or "No Found")
    HTTP:reply{
        status = 200,
        headers = {
            ["content-type"] = { "text/html" },
            ["cache-control"] = { "no-cache", "no-store" },
        },
        body = body,
    }    
end
function check_ip_geo(HTTP)
    local v = HTTP:arg("ip")
    if v then v = ip.addr(v) end -- convert string to ip
    if not v then return reply_invalid(HTTP) end
    local geo = ip.geo(v)
    local geo_code = ip.geo_code(v)
    local body = string.format("<html><body><h1>GEO of IP %s: %s, code: %s<h1></body></html>",
        v, geo, geo_code)
HTTP:reply{
    status = 200,
    headers = {
        ["content-type"] = { "text/html" },
        ["cache-control"] = { "no-cache", "no-store" },
    },
    body = body,
    }
end
when RULE_INIT {
    actions = {}
    actions["reputation"] = check_ip_reputation
    actions["geo"] = check_ip_geo
    convert = {}
    convert["testing"] = "test"
    convert["debugging"] = "debug"
    whitelist = {}
    whitelist["test"] = true
    whitelist["debug"] = true
    whitelist["others"] = true
}
when HTTP_REQUEST {
    local path = HTTP:path()
    path = path:gsub("^/api/", "/api2/") -- convert /api/ to /api2/
    local api = path:match("^/api2/(.+)") -- get api string that is after /api2/
    -- check api
    if api then
        if actions[api] then -- if api is in table "actions", run function
            return actions[api](HTTP)
        end
        if convert[api] then -- if api is in table "convert", convert api
            api = convert[api] -- change to new api
        end
        if not whitelist[api] then -- if api is not in whitelist, reply invalid
            return reply_invalid(HTTP)
        end
        HTTP:set_path("/api2/" .. api) -- pass the api to server
        return
    end
    -- if path doesn't starts with /api or /api2, do nothing
}

HTTP:close()

Close the current HTTP transaction and disable its HTTP events.

Syntax
HTTP:close()
Arguments

N/A

Events

Applicable in HTTP_REQUEST.

Example
when HTTP_REQUEST {
    local path = HTTP:path()
    HTTP:close()
    local url = HTTP:url()
}

HTTP:is_https()

Return true if the current transaction is an HTTPS connection.

Syntax
HTTP:is_https()
Arguments

N/A

Events

Applicable in HTTP_REQUEST, HTTP_RESPONSE, HTTP_DATA_REQUEST, and HTTP_DATA_RESPONSE

Example
when HTTP_REQUEST {
    debug("current transaction is HTTPS connections: %s", HTTP:is_https())
}

HTTP:setpriv(object)

Store a lua object as the HTTP transaction private data.

Syntax
HTTP:setpriv(object)
Arguments

Name

Description

object

Lua object

Events

Applicable in HTTP_REQUEST.

Example
when HTTP_REQUEST {
    store_data = "test"
    HTTP:setpriv(store_data)
}

HTTP:priv()

Fetch the transaction private data that stored by HTTP:setpriv(). If no result is found, it will return an empty lua table.

Syntax
HTTP:priv()
Arguments

N/A

Events

Applicable in HTTP_RESPONSE.

Example
when HTTP_REQUEST {
    store_data = "test"
    HTTP:setpriv(store_data)
}
when HTTP_RESPONSE {
    debug("stored_data = %s", HTTP:priv())
}

HTTP:skip_waf()

Use this Lua script to instruct FortiWeb to bypass WAF module inspection based on HTTP request or response content. This is especially useful when specific URL patterns, headers, or body contents are known to be safe but would otherwise be flagged by WAF rules.

When called in HTTP_REQUEST or HTTP_REQUEST event, skips all WAF modules(except for layer3 session level checked WAF modules).

When called in HTTP_DATA_REQUEST or HTTP_DATA_RESPONSE with partial data collect, skips follow-up WAF modules.

When called in HTTP_DATA_REQUEST or HTTP_DATA_RESPONSE with full data collect, skips remaining modules after LUA_body module follow-up processing.

Syntax
HTTP:skip_waf()
Arguments

N/A

Events

Applicable in HTTP_REQUEST, HTTP_RESPONSE, HTTP_DATA_REQUEST, HTTP_DATA_RESPONSE.

Example

Skip WAF with partial data collect

when HTTP_REQUEST {
    HTTP:collect(32)
}

when HTTP_DATA_REQUEST {
    HTTP:skip_waf()
}

Skip WAF with full data collect

when HTTP_REQUEST {
    HTTP:collect()
}
when HTTP_DATA_REQUEST {
    HTTP:skip_waf()
}

Skip WAF with header

when HTTP_REQUEST {
    local method = HTTP:method()
if method == "POST" then 
    HTTP:skip_waf()
        debug("Bypassing WAF for POST method: %s\n", uri)
    end
}

HTTP Commands

HTTP Commands

HTTP:redirect(fmt, …)

Reply to client with redirect response.

Syntax
HTTP:redirect(fmt, …)
Arguments

Name

Description

fmt

String Type Format.

Events

Applicable in HTTP_REQUEST.

Example
when HTTP_REQUEST {
    HTTP:redirect("https://%s", HTTP:host())
}

HTTP:reply(response)

Reply to client with custom response.

Syntax
HTTP:reply(response)
Arguments

Name

Description

response

Argument response is a lua array. It includes:

  • status: Integer. Default is 200.

  • reason: String. If not set, the system will use the default value of status code. For example, if the status code is 200, the default value of reason is "OK".

  • headers: Lua table. Each value of the table is a lua array. It contains all headers except "content-length". "content-length" will be automatically set with the body size.

  • Body: String.

    To be specific:

    HTTP:reply{

    status = 400,

    reason = "test reason",

    headers = {

    ["content-type"] = { "text/html" },

    ["cache-control"] = { "no-cache", "no-store" },

    },

    body = "<html><body><h1>invalid request<h1></body></html>",

    }

Events

Applicable in HTTP_REQUEST.

Example
function reply_invalid(HTTP)
    HTTP:reply{
        status = 400,
        headers = {
            ["content-type"] = { "text/html" },
            ["cache-control"] = { "no-cache", "no-store" },
        },
        body = "<html><body><h1>Invalid API Request<h1></body></html>",
    }
end
function check_ip_reputation(HTTP)
    local v = HTTP:arg("ip")
    if v then v = ip.addr(v) end -- convert string to ip
    if not v then return reply_invalid(HTTP) end
    local r = ip.reputation(v)
    local body = string.format("<html><body><h1>Reputation of IP %s: %s<h1></body></html>",
        v, #r > 0 and table.concat(r, ', ') or "No Found")
    HTTP:reply{
        status = 200,
        headers = {
            ["content-type"] = { "text/html" },
            ["cache-control"] = { "no-cache", "no-store" },
        },
        body = body,
    }    
end
function check_ip_geo(HTTP)
    local v = HTTP:arg("ip")
    if v then v = ip.addr(v) end -- convert string to ip
    if not v then return reply_invalid(HTTP) end
    local geo = ip.geo(v)
    local geo_code = ip.geo_code(v)
    local body = string.format("<html><body><h1>GEO of IP %s: %s, code: %s<h1></body></html>",
        v, geo, geo_code)
HTTP:reply{
    status = 200,
    headers = {
        ["content-type"] = { "text/html" },
        ["cache-control"] = { "no-cache", "no-store" },
    },
    body = body,
    }
end
when RULE_INIT {
    actions = {}
    actions["reputation"] = check_ip_reputation
    actions["geo"] = check_ip_geo
    convert = {}
    convert["testing"] = "test"
    convert["debugging"] = "debug"
    whitelist = {}
    whitelist["test"] = true
    whitelist["debug"] = true
    whitelist["others"] = true
}
when HTTP_REQUEST {
    local path = HTTP:path()
    path = path:gsub("^/api/", "/api2/") -- convert /api/ to /api2/
    local api = path:match("^/api2/(.+)") -- get api string that is after /api2/
    -- check api
    if api then
        if actions[api] then -- if api is in table "actions", run function
            return actions[api](HTTP)
        end
        if convert[api] then -- if api is in table "convert", convert api
            api = convert[api] -- change to new api
        end
        if not whitelist[api] then -- if api is not in whitelist, reply invalid
            return reply_invalid(HTTP)
        end
        HTTP:set_path("/api2/" .. api) -- pass the api to server
        return
    end
    -- if path doesn't starts with /api or /api2, do nothing
}

HTTP:close()

Close the current HTTP transaction and disable its HTTP events.

Syntax
HTTP:close()
Arguments

N/A

Events

Applicable in HTTP_REQUEST.

Example
when HTTP_REQUEST {
    local path = HTTP:path()
    HTTP:close()
    local url = HTTP:url()
}

HTTP:is_https()

Return true if the current transaction is an HTTPS connection.

Syntax
HTTP:is_https()
Arguments

N/A

Events

Applicable in HTTP_REQUEST, HTTP_RESPONSE, HTTP_DATA_REQUEST, and HTTP_DATA_RESPONSE

Example
when HTTP_REQUEST {
    debug("current transaction is HTTPS connections: %s", HTTP:is_https())
}

HTTP:setpriv(object)

Store a lua object as the HTTP transaction private data.

Syntax
HTTP:setpriv(object)
Arguments

Name

Description

object

Lua object

Events

Applicable in HTTP_REQUEST.

Example
when HTTP_REQUEST {
    store_data = "test"
    HTTP:setpriv(store_data)
}

HTTP:priv()

Fetch the transaction private data that stored by HTTP:setpriv(). If no result is found, it will return an empty lua table.

Syntax
HTTP:priv()
Arguments

N/A

Events

Applicable in HTTP_RESPONSE.

Example
when HTTP_REQUEST {
    store_data = "test"
    HTTP:setpriv(store_data)
}
when HTTP_RESPONSE {
    debug("stored_data = %s", HTTP:priv())
}

HTTP:skip_waf()

Use this Lua script to instruct FortiWeb to bypass WAF module inspection based on HTTP request or response content. This is especially useful when specific URL patterns, headers, or body contents are known to be safe but would otherwise be flagged by WAF rules.

When called in HTTP_REQUEST or HTTP_REQUEST event, skips all WAF modules(except for layer3 session level checked WAF modules).

When called in HTTP_DATA_REQUEST or HTTP_DATA_RESPONSE with partial data collect, skips follow-up WAF modules.

When called in HTTP_DATA_REQUEST or HTTP_DATA_RESPONSE with full data collect, skips remaining modules after LUA_body module follow-up processing.

Syntax
HTTP:skip_waf()
Arguments

N/A

Events

Applicable in HTTP_REQUEST, HTTP_RESPONSE, HTTP_DATA_REQUEST, HTTP_DATA_RESPONSE.

Example

Skip WAF with partial data collect

when HTTP_REQUEST {
    HTTP:collect(32)
}

when HTTP_DATA_REQUEST {
    HTTP:skip_waf()
}

Skip WAF with full data collect

when HTTP_REQUEST {
    HTTP:collect()
}
when HTTP_DATA_REQUEST {
    HTTP:skip_waf()
}

Skip WAF with header

when HTTP_REQUEST {
    local method = HTTP:method()
if method == "POST" then 
    HTTP:skip_waf()
        debug("Bypassing WAF for POST method: %s\n", uri)
    end
}