{"id":50428175,"url":"https://github.com/ryo-yamada/tsua","last_synced_at":"2026-05-31T12:00:55.719Z","repository":{"id":347491060,"uuid":"1194107339","full_name":"ryo-yamada/tsua","owner":"ryo-yamada","description":"Tiny, minimalistic HTTP web server framework made in Lua","archived":false,"fork":false,"pushed_at":"2026-05-28T16:32:23.000Z","size":35,"stargazers_count":1,"open_issues_count":3,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-28T18:12:47.277Z","etag":null,"topics":["framework","http","lua","luasocket","minimalistic","server","simple","tsua","web","webapp","webframework","webserver"],"latest_commit_sha":null,"homepage":"","language":"Lua","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ryo-yamada.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-27T23:39:01.000Z","updated_at":"2026-05-28T17:19:01.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ryo-yamada/tsua","commit_stats":null,"previous_names":["ryo-yamada/tsua"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ryo-yamada/tsua","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ryo-yamada%2Ftsua","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ryo-yamada%2Ftsua/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ryo-yamada%2Ftsua/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ryo-yamada%2Ftsua/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ryo-yamada","download_url":"https://codeload.github.com/ryo-yamada/tsua/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ryo-yamada%2Ftsua/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33730241,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-31T02:00:06.040Z","response_time":95,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["framework","http","lua","luasocket","minimalistic","server","simple","tsua","web","webapp","webframework","webserver"],"created_at":"2026-05-31T12:00:55.110Z","updated_at":"2026-05-31T12:00:55.713Z","avatar_url":"https://github.com/ryo-yamada.png","language":"Lua","funding_links":[],"categories":[],"sub_categories":[],"readme":"# tsua - Tiny Lua webserver framework\nTsua is a tiny, minimalistic HTTP server framework built on top of LuaSocket.\n\nThe framework allows you to simply serve files and send data over HTTP for something like a website, such as a portfolio, without messing with raw, low-level HTTP too much. It works similar to Express.js or Python's Flask.\n\nBecause Lua's runtime is pretty small compared to something like Python's Flask, tsua runs well on stuff like old devices, a tiny VPS, or even microcontrollers such as an ESP32 or Raspberry Pi Pico.\n\n## Current status\nTsua is experimental and only encouraged for static websites where dynamic activity is limited. Security hardening is something I want to gladly work towards. When deploying publicly with tsua, it is recommended that you run it in a containerized environment like Docker.\n\n## Use guide\n- You must have Lua and LuaSocket installed\n\ne.g. `luarocks install luasocket`\n- You can simply download `tsua.lua` and require it in your `server.lua` file\n- No dependencies beyond LuaSocket are required.\n\nBasic example of a `server.lua` file (examples/static-site/server.lua):\n```lua\nlocal Tsua = require(\"tsua\")\nlocal app = Tsua.new({ -- init \u0026 config\n    max_headers = 15,\n    timeout = 2,\n    not_found = \"./frontend/404.html\"\n})\n\napp:static(\"/static\", \"./static\")\n\napp:get(\"/\", function(req, res)\n    res:serve(\"./frontend/index.html\")\nend)\n\napp:get(\"/otherpage\", function(req, res)\n    res:serve(\"./frontend/otherpage.html\")\n    if req.query.name then\n        print(req.query.name) -- /otherpage?name=ryo -\u003e \"ryo\"\n    end\nend)\n\napp:get(\"/\u003cpage\u003e\", function(req, res) -- dynamic!!! serves whatever is passed if it is findable!!!\n    res:serve(\"./frontend/\"..req.dyn.page..\".html\")\nend)\n\napp:post(\"/submit\", function(req, res)\n    if req.params.name then\n        print(req.params.name)  -- \"ryo\"\n    end\nend)\n\napp:listen(19999) -- serve on http://0.0.0.0:19999/\n```\n\n### req and res objects\nParse information about the request with *req*:\n```lua\napp:post(\"/sendcredentials\", function(req, res)\n    req.method -- \"POST\"\n    req.path -- \"/sendcredentials\"\n    req.headers -- table of headers\n    req.body -- \"key1=abc\u0026key2=123\"\n    req.params.key1 -- \"abc\"\n    req.query.name -- /sendcredentials?name=ryo -\u003e \"ryo\"\nend)\n```\nThese are the only objects that req contains.\n\n*res* wraps the raw socket and provides helper methods:\n```lua\napp:get(\"/\", function(req, res)\n    res:serve(\"index.html\") -- this line right here !!\nend)\n\napp:get(\"/oldpage\", function(req, res)\n    res:send(\"301 Moved Permanently\", {[\"Location\"] = \"/new-page\"}, \"\") -- this one aswell !!\nend)\n```\n### Configuration\nYou can configure an instance from the framework to according to how you want it to be.\n\nYou can see how options are set in the `server.lua` example.\n\nThese are all the configurable options and their defaults:\n```lua\nrequest_logging = config.request_logging ~= false, -- looks weird but it prevents unexpected behavior when setting a config, default is true\nmax_body = config.max_body or (1024 * 1024), -- 1MB default max body in requests\nmax_headers = config.max_headers or 30, -- default 30 max headers possible in requests\ntimeout = config.timeout or 3, -- default 3s before dropping client\nerror_handler = config.error_handler, -- custom error handler function config\nnot_found = config.not_found,  -- path to a custom 404 html file, default is framework-provided page\nforbidden = config.forbidden, -- path to a custom 403 html file, default is framework-provided page\n```\n---\n\n## Contributing\n\nContribution is heavily encouraged, and I'm trying to keep tsua small and approachable for this reason\n\nIf you are:\n- learning Lua\n- interested in HTTP\n- looking for something to contribute to\n\nthen I'm looking forward to working with you.\n\nIssues, pull requests, feature suggestions, and discussion are all appreciated. If you're looking to contribute but aren't sure how, you can check the issue tracker, in which I or others might post some issues for you to look into.\n\nDevelopment is currently focused on:\n- Security\n- Dynamic support\n- Developer experience enhancements\n\n## Philosophy\nThis was a project I started just for fun, as I wanted to see how easy it would be to deliver a site over the web with a Lua backend. However, it started becoming its own little framework, and so I decided to publish it on GitHub. My plan is to continue developing it with a focus on simplicity.\n\nThis framework is aimed towards sites that don't have too much dynamic functionality. Portfolios, documentation, etc. However, I suppose when this framework continues development, it will probably be able to support more dynamic functionality.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fryo-yamada%2Ftsua","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fryo-yamada%2Ftsua","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fryo-yamada%2Ftsua/lists"}