{"id":34204968,"url":"https://github.com/negasus/traefik2-luascript","last_synced_at":"2026-03-11T07:31:17.333Z","repository":{"id":47311327,"uuid":"179164928","full_name":"negasus/traefik2-luascript","owner":"negasus","description":"LuaScript middleware for Traefik v2","archived":false,"fork":false,"pushed_at":"2021-11-30T09:42:48.000Z","size":44,"stargazers_count":64,"open_issues_count":2,"forks_count":6,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-06-19T00:20:23.785Z","etag":null,"topics":["lua","middleware","traefik"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/negasus.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-04-02T21:57:33.000Z","updated_at":"2024-01-10T23:42:15.000Z","dependencies_parsed_at":"2022-09-11T03:01:10.583Z","dependency_job_id":null,"html_url":"https://github.com/negasus/traefik2-luascript","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/negasus/traefik2-luascript","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/negasus%2Ftraefik2-luascript","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/negasus%2Ftraefik2-luascript/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/negasus%2Ftraefik2-luascript/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/negasus%2Ftraefik2-luascript/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/negasus","download_url":"https://codeload.github.com/negasus/traefik2-luascript/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/negasus%2Ftraefik2-luascript/sbom","scorecard":{"id":678609,"data":{"date":"2025-08-11","repo":{"name":"github.com/negasus/traefik2-luascript","commit":"75cf96c1128a10a4fa9572b371b5703a4acbb523"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.6,"checks":[{"name":"Code-Review","score":0,"reason":"Found 1/17 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":0,"reason":"license file not detected","details":["Warn: project does not have a license file"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 1 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-21T22:22:30.495Z","repository_id":47311327,"created_at":"2025-08-21T22:22:30.495Z","updated_at":"2025-08-21T22:22:30.495Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30374209,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-11T06:09:32.197Z","status":"ssl_error","status_checked_at":"2026-03-11T06:09:17.086Z","response_time":84,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["lua","middleware","traefik"],"created_at":"2025-12-15T19:34:44.108Z","updated_at":"2026-03-11T07:31:17.320Z","avatar_url":"https://github.com/negasus.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Traefik2 LuaScript\n\n**LuaScript** is middleware for [Traefik v2](https://github.com/containous/traefik) for execute lua script with access to API\n\nUnder cover used LUA VM from [Yusuke Inuzuka](https://github.com/yuin/gopher-lua)\n\nAn [issue](https://github.com/containous/traefik/issues/1336#issuecomment-478517290)  \n\nPost on [the community portal](https://community.containo.us/t/custom-middleware-for-traefik2-lua-script/5463) \n\n## About\n\nThis middleware allows you to write your business logic in LUA script\n\n- get an incoming request\n- get/set request body\n- add/modify request headers\n- add/modify response headers\n- interrupt the request\n- make HTTP calls to foreign services\n- write to traefik log\n- encode/decode json\n \n## Usage example\n\n```lua\n-- middleware_example.lua\n\nlocal traefik = require('traefik')\nlocal log = require('log')\n\nlocal h, err = traefik.getRequestHeader('X-Some-Header')\nif err ~= nil then\n  log.warn('error get header ' .. err)\n  return\nend\n\nif h == '' then\n    traefik.interrupt(401, 'HTTP Header empty or not exists')\n    return\nend\n\ntraefik.setRequestHeader('Authorized', 'SUCCESS')\n\nlog.info('continue')\n```\n\nFunctions may return an error as a last variable.\nIt is a string with an error message or `nil`, if no error \n\n## Benchmark\n\n\u003e See into `benchmark` folder in this repo\n\nBackend is a simple go application\n\n```go\npackage main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n)\n\nvar ok = []byte(\"ok\")\n\nfunc handler(w http.ResponseWriter, r *http.Request) {\n\tw.WriteHeader(200)\n\tw.Write(ok)\n}\n\nfunc main() {\n\thttp.HandleFunc(\"/\", handler)\n\tlog.Printf(\"listen 2000\")\n\thttp.ListenAndServe(\"127.0.0.1:2000\", nil)\n}\n```\n\nRun load testing with [vegeta](https://github.com/tsenart/vegeta)\n\n```bash\necho \"GET http://localhost/\" | vegeta attack -rate 2000 -duration=60s | tee results.bin | vegeta report\n```\n\n**With LUA**\n\nA Traefik config\n\n```yaml\nhttp:\n  routers:\n    router1:\n      rule: \"Host(`localhost`)\"\n      service: service1\n      middlewares:\n        - example\n\n  middlewares:\n    example:\n      luascript:\n        script: middleware.lua\n\n  services:\n    service1:\n      loadBalancer:\n        servers:\n          - url: \"http://127.0.0.1:2000\"\n```\n\nA Lua script\n\n```lua\nlocal traefik = require('traefik')\n\ntraefik.setRequestHeader('X-Header', 'Example')\ntraefik.setResponseHeader('X-Header', 'Example')\n```\n\nA Result\n\n```\nRequests      [total, rate, throughput]  120000, 2000.02, 2000.00\nDuration      [total, attack, wait]      59.999868062s, 59.999484357s, 383.705µs\nLatencies     [mean, 50, 95, 99, max]    471.058µs, 365.053µs, 993.75µs, 1.26782ms, 18.771475ms\nBytes In      [total, mean]              240000, 2.00\nBytes Out     [total, mean]              0, 0.00\nSuccess       [ratio]                    100.00%\nStatus Codes  [code:count]               200:120000\nError Set:\n```\n\n**Without LUA**\n\nA Traefik config\n\n```yaml\nhttp:\n  routers:\n    router1:\n      rule: \"Host(`localhost`)\"\n      service: service1\n\n  services:\n    service1:\n      loadBalancer:\n        servers:\n          - url: \"http://127.0.0.1:2000\"\n```\n\nA Result\n\n```\nRequests      [total, rate, throughput]  120000, 2000.02, 2000.01\nDuration      [total, attack, wait]      59.999708481s, 59.999466875s, 241.606µs\nLatencies     [mean, 50, 95, 99, max]    257.527µs, 227.055µs, 339.606µs, 520.189µs, 28.70824ms\nBytes In      [total, mean]              240000, 2.00\nBytes Out     [total, mean]              0, 0.00\nSuccess       [ratio]                    100.00%\nStatus Codes  [code:count]               200:120000\nError Set:\n```\n\n\n\n## Installation from sources and run\n\nDownload the Traefik sources and go to the directory\n\n```bash\ngit clone https://github.com/traefik/traefik\ncd traefik\n```\n\nAdd this repo as a submodule\n\n```bash\ngit submodule add https://github.com/negasus/traefik2-luascript pkg/middlewares/luascript\n```\n\nAdd the code for the middleware config to the file `pkg/config/dynamic/middleware.go`\n\n```go\ntype Middleware struct {\n  // ...\n\tLuaScript         *LuaScript         `json:\"luascript,omitempty\" toml:\"luascript,omitempty\" yaml:\"luascript,omitempty\"`\n  // ...\n}\n\n// ...\n\n// +k8s:deepcopy-gen=true\n\n// LuaScript config\ntype LuaScript struct {\n\tScript string `json:\"script,omitempty\" toml:\"script,omitempty\" yaml:\"script,omitempty\"`\n}\n```\n\nAdd the code for register a middleware to the file `pkg/server/middleware/middlewares.go`\n\n```go\nimport (\n  // ...\n\t\"github.com/traefik/traefik/v2/pkg/middlewares/luascript\"  \n  // ...\n)\n\n// ...\n\nfunc (b *Builder) buildConstructor(ctx context.Context, middlewareName string, config config.Middleware) (alice.Constructor, error) {\n  // ...\n  \n  // BEGIN LUASCRIPT BLOCK\n\tif config.LuaScript != nil {\n\t\tif middleware == nil {\n\t\t\tmiddleware = func(next http.Handler) (http.Handler, error) {\n\t\t\t\treturn luascript.New(ctx, next, *config.LuaScript, middlewareName)\n\t\t\t}\n\t\t} else {\n\t\t\treturn nil, badConf\n\t\t}\n\t}\n  // END LUASCRIPT BLOCK\n  \n\tif middleware == nil {\n\t\treturn nil, fmt.Errorf(\"invalid middleware %q configuration: invalid middleware type or middleware does not exist\", middlewareName)\n\t}\n\n\treturn tracing.Wrap(ctx, middleware), nil\n}\n```\n\nBuild the Traefik\n\n```bash\nmake build\n```\n\nCreate a config file `config.yml`\n\n```yml\nlog:\n  level: warn\n\nentryPoints:\n  web:\n    address: \":8080\"\n\nproviders:\n  file:\n    filename: \"providers.yml\"\n```\n\nand `providers.yml`\n\n```yml\nhttp:\n  routers:\n    router1:\n      rule: \"Host(`localhost`)\"\n      service: service1\n      middlewares:\n        - example\n\n  middlewares:\n    example:\n      luascript:\n        script: example.lua\n\n  services:\n    service1:\n      loadBalancer:\n        servers:\n          - url: \"https://api.github.com/users/octocat/orgs\"\n```\n\nCreate a lua script `example.lua`\n\n```lua\nlocal traefik = require('traefik')\nlocal log = require('log')\n\nlog.warn('Hello from LUA script')\ntraefik.setResponseHeader('X-New-Response-Header', 'Woohoo')\n```\n\nRun the traefik\n\n```bash\n./dist/traefik --configFile=config.yml\n```\n\nCall the traefik (from another terminal)\n\n```bash\ncurl -v http://localhost:8080\n```\n\nAnd, as result, we see a traefik log\n\n```\nWARN[...] Hello from LUA script \tmiddlewareName=file.example-luascript middlewareType=LuaScript\n```\n\nA response from the github API with our header\n\n```\n...\n\u003c X-New-Response-Header: Woohoo\n...\n```\n\nDone!\n\n## API\n\n### Traefik\n\n`Traefik` module allows get information about current request. Add request/response headers, or interrupt the request.\n\nUsage:\n\n```lua\ntraefik = require('traefik')\n```\n\n**Get Request Header**\n\n\u003e getRequestHeader(**name** string) **value** string, **error**\n\nIf header not exists, returns no error and empty string value!\n\n```lua \nlocal traefik = require('traefik')\nlocal log = require('log')\n\nlocal h, err = traefik.getRequestHeader('X-Authorization')\nif err ~= nil then\n  log.debug('error get header' .. err)\nend\n```\n\n**Set Request Header**\n\n\u003e setRequestHeader(**name** string, **value** string) **error**\n\nSet header for pass to backend\n\n```lua \nerr = traefik.setRequestHeader('X-Authorization', 'SomeSecretToken')\n```\n\n**Get Request Body**\n\n\u003e getRequestBody() **value** string, **error**\n\n```lua \nlocal body, err = traefik.getRequestBody()\nif err ~= nil then\n  log.debug('error get body ' .. err)\nend\n```\n\n**Set Request Body**\n\n\u003e setRequestBody(**value** string) **error**\n\nSet body for pass to backend\n\n```lua \nerr = traefik.setRequestBody('{\"foo\": \"bar\"}')\n```\n\n**Set Response Header**\n\n\u003e setResponseHeader(**name** string, **value** string) **error**\n\nSet header for return to client\n\n```lua \nerr = traefik.setResponseHeader('X-Authorization', 'SomeSecretToken')\n```\n\n** Interrupt the request and return StatusCode and  Body\n\n\u003e interrupt(**code** int, [**message** string]) **error**\n\n```lua \nerr = traefik.interrupt(403)\n\n-- or\n\nerr = traefik.interrupt(422, 'Validation Error')\n```\n\n**Get Request Query Argument**\n\n\u003e getQueryArg(**name** string) **value** string, **error**\n\nGet value from query args\n\n```lua \n-- Get 'foo' for URL http://example.com/?token=foo\nv, err = traefik.getQueryArg('token')\n```\n\n**Get Request**\n\n\u003e getRequest() **value** table\n\nGet request info\n\n```lua \ninfo = traefik.getRequest()\n\n{\n    method = 'GET',\n    uri = '...',\n    host = '...',\n    remoteAddr = '...',\n    referer = '...',\n    headers = {\n        key = 'value',\n        ...\n    }\n}\n```\n\n### LOG\n\nSend a message to a traefik logger\n\n\u003e error(message string)\n\n\u003e warn(message string)\n\n\u003e info(message string)\n\n\u003e debug(message string)\n\n```lua\nlocal log = require('log')\n\nlog.error('an error occured')\nlog.debug('header ' .. h .. ' not exist')\n```\n\n### HTTP\n\nSet HTTP requests to remote services\n\nUsage: \n\n```lua\nhttp = request('http')\n```\n\n**request**\n\n\u003e request(**\u003cOPTIONS\u003e** table) **response**[, **error** string]\n\nSend a request\n\n**OPTIONS** is a table with request options\n\n```\n{\n    method = 'POST',    -- http method. By default: GET\n    url     = '',       -- URL\n    body    = '',       -- request body. By default: empty\n    timeout = 100,      -- timeout in milliseconds. By default: 250 (ms)\n    headers = {         -- request heders\n        key = 'value',\n        ...\n    }\n}\n```\n\n**RESPONSE**\n\n```\n{\n    status  = 200,      -- response status code\n    body    = '',       -- response body\n    headers = {         -- response headers\n        key = value,\n        ...\n    }\n}\n```\n\n**get, post, put, delete**\n\n\u003e get('url', [OPTIONS]) **response**[, **error** string]\n\n\u003e post('url', [OPTIONS]) **response**[, **error** string]\n\n\u003e put('url', [OPTIONS]) **response**[, **error** string]\n\n\u003e delete('url', [OPTIONS]) **response**[, **error** string]\n\nAliases for `request` with predefined Method and URL\n\n### JSON\n\n**Decode**\n\nDecodes a JSON string. Returns nil and an error string if the string could not be decoded\n\n\u003e decode(**str** string) **value** value, **error**\n\nThe following types are supported:\n\n Lua      | JSON     \n -------- | -------- \n  table   | object: when table is non-empty and has only string keys; array:  when table is empty, or has only sequential numeric keys starting from 1 \n  string  | string\n  number  | number\n  nil     | null\n\n\n```lua \nlocal json = require('json')\nvalue, err = json.decode('{\"foo\": \"bar\"}')\n```\n\n**Encode**\n\nEncodes a value into a JSON string. Returns nil and an error string if the value could not be encoded\n\n\u003e encode(**value** value) **str** string, **error**\n\n```lua \nlocal json = require('json')\nt = {}\nt[1] = \"first\" \nstr, err = json.encode(t)\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnegasus%2Ftraefik2-luascript","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnegasus%2Ftraefik2-luascript","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnegasus%2Ftraefik2-luascript/lists"}