A HTTP/1.0 static file server written in NetWide Assembler, by Douxx.
Read the blog article!
Caution
Educational project: This server likely has more security flaws than there are stars in the universe. It is not recommended for production use. Use it to learn, experiment, or have fun.
I started learning NASM on a Monday afternoon, because I was bored in my NoSQL class. After a few self-made exercises (parsing args, string processing, etc.), I built a small HTTP client tool, imagine curl, but without the cool stuff. Then during my blockchain class, I started developing this server. The source grew from there.
- HTTP/1.0
GETandHEADrequests - Static file serving with automatic MIME type detection (70+ types)
- Basic Authentication (
Authorization: Basic) - Custom error pages per status code (
400,401,403,404,405) - Configurable
Cache-ControlviaMax-Age/Expiresheaders Last-Modifiedheader based on file mtime- Dotfile/dotfolder serving control
- Concurrent request handling via
fork() - Configurable
Server:header - Per-request logging with timestamps and client IPs
- HTTP/1.1 (keep-alive, chunked transfer, etc.). Responses are always
HTTP/1.0 - HTTPS / TLS, use a reverse proxy (nginx, Caddy) or a Cloudflare tunnel in front
- Dynamic content: no CGI, no scripting, purely static
- CRLF line endings in
.envfiles: use LF only - Range requests (
Range: bytes=...) - Directory listing, directories without an index file return
403 - Query string processing,
?foo=baris stripped and ignored since it's a static file host
Each release ships prebuilt bundles for different architectures. The bundles contain everything you need to run NASMServer, including the entry script nasmserver, an example config file env.example, and a default web directory www/.
# 1. Download and extract the release
wget https://github.com/douxxtech/nasmserver/releases/latest/download/nasmserver-linux-x64.zip
unzip nasmserver-linux-x64.zip -d nasmserver
# 2. Enter the directory
cd nasmserver
# 3. Read the instructions
cat instructions.txt
# 4. Copy the example config (optional)
cp env.example .env
# 5. Run it (defaults to port 8080, document root: ./www or current directory)
./nasmserver
# Pass a custom config file
./nasmserver -e /path/to/config.env
# See all supported flags
./nasmserver -hPorts below 1024 require root or
CAP_NET_BIND_SERVICE. Either run withsudo, or setPORTto something above 1024 in your.env.
| Flag | Description |
|---|---|
-h |
Show help and exit |
-v |
Show version and exit |
-e <path> |
Path to the .env config file to load |
Each bundle includes an install script that sets up NASMServer system-wide. It can also be used to update an existing install without overwriting your config.
# Once inside the extracted bundle (see above)
./installRequirements: nasm binutils patchelf
# Build the x86_64 binary only
bash buildasm.sh program.asm
# Output binary: ./program
# Build a full release bundle
bash .github/scripts/build-bundle.sh <x64|aarch64>
# Outputs: bundle-<arch>/ and nasmserver-linux-<arch>.zipThe entry point is program.asm. Macros and utilities live in macros/, labels in labels/.
Copy env.example to .env and edit as needed. All keys are optional and defaults apply if a key is missing or the file is not found.
| Key | Default | Description |
|---|---|---|
PORT |
8080 |
Port to listen on |
DOCUMENT_ROOT |
. |
Document root directory, no trailing slash |
INDEX_FILE |
index.html |
File served when a directory is requested |
MAX_AGE |
600 |
Cache expiry offset in seconds for the Expires: header. Sets Pragma: no-cache if 0 |
AUTH_USER |
(empty) | Username for Basic Authentication. Authentication is only enabled when this is set |
AUTH_PASSWORD |
(empty) | Password for Basic Authentication. Only used if AUTH_USER is set |
SERVE_DOTS |
false |
Whether .dotfiles and .dotfolders/ should be served |
MAX_REQUESTS |
20 |
Max simultaneous connections (1–65535) |
SERVER_NAME |
NASMServer/ver |
Value for the Server: response header |
ERRORDOC_400 |
(empty) | Error page path, relative to DOCUMENT_ROOT, must start with / |
ERRORDOC_401 |
(empty) | Same, for 401 |
ERRORDOC_403 |
(empty) | Same, for 403 |
ERRORDOC_404 |
(empty) | Same, for 404 |
ERRORDOC_405 |
(empty) | Same, for 405 |
If an ERRORDOC_* is left empty, the server sends headers only with no body for that error. Nonexistent errordoc files produce a startup warning but are not fatal.
- The server uses
fork()per connection, so no threads, no event loop. Each child handles exactly one request, then exits. - Zombie reaping happens at the top of the
accept()loop, so zombies linger until the next incoming connection. - Concurrent connections are capped by
MAX_REQUESTS. If the limit is hit, the connection is dropped and a warning is logged. - The request buffer is 8 KB. Requests larger than that are truncated.
- Path traversal (
..) is blocked in the path parser. Dotfile access is blocked by default unlessSERVE_DOTS=true. - Startup checks validate
DOCUMENT_ROOTexistence and permissions, and warn on missing errordoc files. A badDOCUMENT_ROOTis fatal. - No CRLF support in
.envfiles, be sure to use LF line endings only.
- HTTP/1.0 only, no TLS (HTTPS can be handled upstream via a reverse proxy or Cloudflare tunnel)
- Bugs, errors, or issues? Open an issue
See LICENSE for usage terms.