{"id":13537029,"url":"https://github.com/remileduc/sherver","last_synced_at":"2025-04-09T18:20:25.753Z","repository":{"id":75661992,"uuid":"192119451","full_name":"remileduc/sherver","owner":"remileduc","description":"Pure Bash lightweight web server.","archived":false,"fork":false,"pushed_at":"2020-01-07T08:49:32.000Z","size":649,"stargazers_count":343,"open_issues_count":2,"forks_count":11,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-02T12:49:58.717Z","etag":null,"topics":["bash","lightweight","server","web"],"latest_commit_sha":null,"homepage":null,"language":"Shell","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/remileduc.png","metadata":{"files":{"readme":"README.md","changelog":null,"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}},"created_at":"2019-06-15T20:05:16.000Z","updated_at":"2025-03-15T05:22:18.000Z","dependencies_parsed_at":"2023-06-07T07:15:39.301Z","dependency_job_id":null,"html_url":"https://github.com/remileduc/sherver","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/remileduc%2Fsherver","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/remileduc%2Fsherver/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/remileduc%2Fsherver/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/remileduc%2Fsherver/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/remileduc","download_url":"https://codeload.github.com/remileduc/sherver/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248085324,"owners_count":21045139,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["bash","lightweight","server","web"],"created_at":"2024-08-01T09:00:53.749Z","updated_at":"2025-04-09T18:20:25.715Z","avatar_url":"https://github.com/remileduc.png","language":"Shell","readme":"\u003c!--\nMIT License\n\nSherver: Pure Bash lightweight web server.\nCopyright (c) 2019 Rémi Ducceschi\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n--\u003e\n\nSherver\n=======\n\nPure Bash lightweight web server.\n\nEasy solution to setup a **local** website without any server configuration!!!\n\nThis is inspired by [bashttpd](https://github.com/avleen/bashttpd). Though, the behavior is entirely different. See below for more information.\n\n[Presentation](#presentation)\n- [How to run](#how-to-run)\n- [Requirements](#requirements)\n- [Features](#features)\n\n[How to use](#how-to-use)\n- [Serve static pages](#serve-static-pages)\n- [Serve files](#serve-files)\n- [Serve dynamic pages](#serve-dynamic-pages)\n- [Template mechanism](#template-mechanism)\n- [POST requests](#post-requests)\n\n[How to use (Expert)](#how-to-use-expert)\n- [Logs](#logs)\n- [Dispatcher](#dispatcher)\n- [Run as a service (daemon)](#run-as-a-service-daemon)\n\n[Example](#example)\n\n[About Security](#about-security)\n\n[Why Sherver?](#why-sherver)\n\n[License?](#license)\n\n\nPresentation\n------------\n\n### How to run ###\n\nJust clone and run `./sherver.sh`. Then, you should be able to connect to [localhost:8080](http://localhost:8080/). You can pass the port to listen on as a parameter: `./sherver.sh 8080` (default is `8080`).\n\n### Requirements ###\n\nThis is made to run with `Bash`. It may not work in another shell. The following tools need to be present in the system (note that they are all part of the default installation of Debian):\n- `envsubst` if you want to do templating\n- `socat` to run the server.\n\t- you can use `netcat` instead, but it doesn't work well with concurrent HTTP requests\n\n### Features ###\n\nSherver is a web server that implements part of HTTP 1.0. Even if it is written in a few lines of Bash, it is able to do a lot:\n- no configuration needed: you can just add files either in `scripts` or in `file` folders\n- serve any HTML page no matter how complexe (with advanced JavaScript and multiple scripts or files to download...)\n- serve files (text or binary, pictures...) with correct mime type\n- dynamic pages\n- templated HTML so you don't have to duplicate headers and footers\n- parse of URL query string\n- support for GET and POST\n- deal with client cache resources\n- easily extandable\n\t- can run any scripts or executable of any languages as soon as they output something on `stdout`\n\t- comes with a library of bash functions to ease the use\n\nAll of these makes Sherver the perfect tool to run a small server that will serve few pages on your local network.\n\nEven if it sounds awesome, Sherver still has the following limitations:\n- only support HTTP GET and POST requests, though it would be easy to add the others\n- no concurrency\n\t- if a page needs to download a lot of files, the files are sent one after the other\n\t- if 2 users access the website, the second one needs to wait until the first one is fully served\n- no security (see [About Security](#about-security)).\n\nThis is why Sherver is supposed to remain in a private and controlled environment. **Do not expose Sherver on Internet!!!** If you want to expose your site on Internet, you should use a tool that knows about security and concurrency (like *nginx* or other).\n\n**Always run Sherver behind a firewall that prevent any intrusions from outside**.\n\nHow to use\n----------\n\nQuick documentation about how to use Sherver for your own use. All variables and functions mentioned here have a full\ndescription in [scripts/README.md](./scripts/README.md).\n\n### Serve static pages ###\n\nThe simplest thing you can do is to serve static pages : pure HTML files that don't need any processing.\n\nTo do so, you only need to put your HTML files in the subdirectory [file/pages](./file/pages). Then, you can access\nto your pages through a URL like `/file/pages/index.html` (if your\nfile name is `index.html` for instance).\n\nNote that you'll have to give the full file name in the URL so Sherver can find it.\n\nIt is as simple as that! If Sherver can find the file, it will serve it. Otherwise, it will return a 404 error.\n\n### Serve files ###\n\nYou can serve any type of files from Sherver. From text-based like CSS or JavaScript to binaries like images, videos, zip...\n\nJust put the files in the subdirectory [file](./file). You can then reference them through a URL like\n`/file/venise.webp`. Note that it is preferable to give full path rather than relative paths.\n\nSherver will automatically serve the file if it can find it, with the correct mime type. It will even allow the browser to\ncache the file, and will only serve it again if the file has changed. If Sherver can't find the file, it will return a\n404 error.\n\nFor resources, like CSS, JavaScript, favicon... it is better to put them in the subfolder [file/resources](./file/resources),\nthough you don't have to.\n\n**Example on how to link a CSS file:**\n\n```html\n\u003clink rel=\"stylesheet\" type=\"text/css\" href=\"/file/resources/ugly.css\"\u003e\n```\n\n**Example on how to integrate a picture in your HTML:**\n\n```html\n\u003cimg src=\"/file/venise.webp\" alt=\"\"\u003e\n```\n\n### Serve dynamic pages ###\n\nThis is where Sherver becomes useful: it can serve dynamic pages, built server side depending on the context.\n\nTo do so, you just need to add executables in the subfolder [scripts](./scripts). Executables can be of any types\n(bash script, python script, any other scripts, any binary like C++ compiled executable...) as soon as Sherver can\nexecute it (it must have the `executable` flag set).\n\nAs soon as you have an executable there, Sherver will run it and serve its output. Note that `index.sh` is a\nparticular name as it is the one that will be executed by the dispatcher if you access to the root of the website\n(see [dispatcher](#dispatcher) section below). If Sherver can't run any files, it will return a 404 error. If\nthe executable fails (return code is not `0`), it will return a 500 error.\n\nTo link an executable, you have to omit the folder`scripts` in the URL: `/page.sh` will look for the executable\n`./scripts/page.sh`.\n\nThe executable is ran from the `scripts` folder.\n\n**Bash scripts**\n\nSherver is mainly made to work with bash scripts. If you create a Bash script, the first thing you should do is to\nrun the function `init_environment`. Then you will have access to all the following variables:\n- `REQUEST_FULL_STRING`\n- `REQUEST_METHOD`\n- `REQUEST_URL`\n- `REQUEST_HEADERS`\n- `REQUEST_BODY`\n- `URL_BASE`\n- `URL_PARAMETERS`\n- `DATE`\n- `RESPONSE_HEADERS`\n- `HTTP_RESPONSE`\n\nAnd also a lot of useful functions like:\n- `add_header`\n- `send_response`\n- `send_file`\n- `send_error`\n\nCheck the whole documentation about the `SHERVER_UTILS.sh` library in [scripts/README.md](./scripts/README.md).\n\nEverything written on the standard output will be sent to the client. Here is a very simple script that returns\nthe requests in a text format:\n\n```bash\n#!/bin/bash\n\ninit_environment\nif [ \"$REQUEST_METHOD\" != 'GET' ]; then\n\tsend_error 405\nfi\n\nadd_header 'Content-Type' 'text/plain'\nsend_response 200 \"$REQUEST_FULL_STRING\"\n```\n\n**Any other scripts or binaries**\n\nIf you don't use Bash, you will only have access to the environment variable `REQUEST_FULL_STRING` that\ncontains the full request as a string. The requested URL (`REQUEST_URL`) will be passed as first argument.\n\nEverything written on the standart output will be sent to the client. Though, you should write the\nheaders of the response yourself.\n\n### Template mechanism ###\n\nFor Bash scripts, there is a basic template engine integrated with Sherver (lol). It actually uses\n`envsubst` to replace any occurrence of `$VARIABLE` by the variable from the environment if there is.\n\nYou can put your templates in the subfolder [scripts/templates](./scripts/templates), though it is not\nmandatory.\n\nHere is a template for a text file `template.txt` (that improves our previous Bash script example):\n\n```\nYou entered the following request:\n\n$REQUEST\n```\n\nAnd you would use it with the following script:\n\n```bash\n#!/bin/bash\n\ninit_environment\nif [ \"$REQUEST_METHOD\" != 'GET' ]; then\n\tsend_error 405\nfi\n\nREQUEST=\"$REQUEST_FULL_STRING\"\n# put REQUEST in the environment so we can use it in our template\nexport REQUEST\n# load the template\nresponse=$(envsubst \u003c 'templates/template.txt')\n\nadd_header 'Content-Type' 'text/plain'\nsend_response 200 \"$response\"\n```\n\nFull HTML example in [Example](#example) below.\n\n\n### POST requests ###\n\nPost requests are supported. You can check the value of the variable `REQUEST_METHOD` that will either be\n`GET` or `POST`, so you can have different behavior based on the type of the request.\n\nThe content of the POST request can be retreived in the variable `REQUEST_BODY`. If the data is url encoded\nby the client, you can use the function `parse_url` with some tricks to get an associative array of the parameters.\n\nAny content can be sent back to the client. You can add the correct mime type thanks to the method `add_header`.\n\nHow to use (Expert)\n-------------------\n\nAll variables and functions mentioned here have a full description in [scripts/README.md](./scripts/README.md).\n\n### Logs ###\n\nAnything written to the standard error can be logged. To ease the logs, you can use the function `log`.\n\nTo keep the logs in a file, you can redirect the error output of *sherver.sh* in a file:\n\n```bash\n./sherver.sh 2\u003e logs.txt\n```\n\nBy default, the headers of both the requests and the responses are logged, but not the bodies.\n\n### Dispatcher ###\n\nThe dispatcher is responsible of asking to either serve a file or run a script, depending on the requested UTL.\nIt is implemented in the file [dispatcher.sh](./dispatcher.sh).\n\nIt currently has 4 actions:\n\n- if the URL is the root (`/`), then it executes the script `scripts/index.sh`\n- if the URL asks for `index.htm` or `index.html`, it executes the script `scripts/index.sh`\n- if the URL starts with `/file/`, it serves the file asked\n- in any other case, it will run the script provided by the URL, prepending the *scripts* folder\n  (the URL `/test/dummy.sh` will run the script `scripts/test/dummy.sh` if it exists).\n\nAll this behaviors can be changed by editing the file [dispatcher.sh](./dispatcher.sh).\n\n### Run as a service (daemon) ###\n\nFirst of all, you need to create a specific user that will run `sherver.sh` with low priviledges.\nWe'll call our user `sherver` and we'll put the whole website in its hone directory.\n\nWe need to add our user to the groups `sudo` and `netdev`, so it is able to manage the VPN\n(it is obviously not a good idea to give `sudo` to the user, this is why you shouldn't expose\nthe website on Internet).\n\n```bash\nuseradd -mUG sudo,netdev -s /usr/bin/bash sherver\npasswd sherver\n\t...\n```\n\nNote that you can add your current user to the `sherver` group for practical reasons\n(you'll have to relog to make it effective):\n```bash\nadduser USER sherver\n```\n\nNow, let's get the website in its home directory\n\n```bash\nsu sherver\ncd ~\ngit clone https://github.com/remileduc/sherver.git\ncd sherver\ngit checkout perso\n```\n\nFinally, we need to enable the service so it starts `sherver.sh` automatically. To do so,\ncopy the file [sherver.service](./sherver.service) in `/usr/share/systemd/` and then\nenable the service:\n\n```bash\ncp sherver.service /usr/share/systemd/\nln -s /usr/share/systemd/sherver.service /etc/systemd/system/sherver.service\nsystemctl daemon-reload\nsystemctl enable sherver.service\n```\n\nExample\n-------\n\nYou can see as an example the scripts that I use at home to manage my VPN. It is accessible on the\n[perso](https://github.com/remileduc/sherver/tree/perso) branch. Note that you need the script\n[vpn-mgr.sh](https://github.com/remileduc/vpn-mgr) to be able to use it properly.\n\nAbout security\n--------------\n\nSee [bashttpd](https://github.com/avleen/bashttpd#security). It is obvious to say that this comes without any security\nfeatures. **Do not expose Sherver on Internet**.\n\n- it is not currently able to serve over HTTPS\n- it uses rudimentary bash scripts to parse URL and POST request body, that could lead to security breaches\n- it executes blindly any script in the *scripts* subfolder\n\nIf you need to expose the site on internet, you need a real server that has been built especially to face all these\nissues.\n\nThough, it is perfect to use on a local network. It will be as secure as are your wifi connection and your firewall.\n\nWhy Sherver?\n------------\n\nI wanted to set up quickly a server that would serve dynamic pages, and that could execute some bash scripts, in order\nto control my media center through web pages.\n\nI didn't want to install and configure Apache or nGinx. In fact, I didn't want *any* configuration.\n\nSherver is able to run without any configuration. You just need to add files at the right place. It can run without\nanything to be installed (all tools used are part of the default installation of Debian, except maybe for socat).\n\nYou can see my use case in the `perso` branche.\n\nLicense\n-------\n\nEverything is under MIT License.\n\nWe use `mimetype` script, shipped in [scripts/utils/mimetype](./scripts/utils/mimetype), which is under Perl license.\n","funding_links":[],"categories":["Downloading and Serving","Shell","Applications"],"sub_categories":["Web Frameworks"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fremileduc%2Fsherver","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fremileduc%2Fsherver","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fremileduc%2Fsherver/lists"}