Minimal, small HTTP server written in C using the Win32/Winsock2 API. It serves static files from a directory and provides a tiny thread pool, access logging (Combined Log Format), and size-based log rotation.
This repository is intended for learning, experimentation, and small internal use-cases. It is not a drop-in production web server.
- Serve a directory of static files (default
public). - Small thread pool (configurable worker count).
- Access logs written in Combined Log Format (CLF) when
--log-fileis used. - Size-based log rotation with
--log-rotate-sizeand--log-rotate-count. - Graceful shutdown on Ctrl+C.
- Minimal CGI support for dynamic content (PHP, Python, Perl, Ruby) via per-request processes.
- PHP works with the
php-cgiwrapper; supply a custom path with--php-cgi.
You can build this project with MinGW (MSYS2) or using CMake. A CMakePresets.json is included with a mingw preset.
Recommended (CMake + MinGW):
PowerShell / cmd (uses the MinGW Makefiles generator):
cmake --preset mingw # configure (uses CMakePresets.json)
cmake --build --preset releaseOr the equivalent manual steps:
cmake -S . -B build -G "MinGW Makefiles"
cmake --build build --config ReleaseYou can also use the provided helper scripts in scripts/:
.\scripts\build.ps1 # PowerShell helper (configures once and builds)If you prefer to compile directly with gcc (MSYS2), the project can still be built with a single gcc command, but using CMake is recommended for consistent artifacts.
When built with CMake the executable is produced under build/Release and is named Cserver.exe (or Cserver on non-Windows generators). The scripts/ helpers start the built binary and provide presets.
Default (serve ../public on port 8080 from repo root using the CMake build output):
.\build\Cserver.exeRecommended (use the start helpers):
PowerShell:
.\scripts\start.ps1 -WithPhp -WithLogscmd.exe:
scripts\start.bat /php /logsExamples (directly invoking the CMake-built executable):
- Serve
publicon port 8080 with 4 workers and write access logs toserver.log:
.\build\Cserver.exe --port 8080 --serve public --workers 4 --log-file .\server.log- Log rotation: rotate logs at ~10 MB and keep 5 rotated files:
.\build\Cserver.exe --log-file .\server.log --log-rotate-size 10M --log-rotate-count 5--port <port>— TCP port to listen on (default 8080).--serve <path>— directory to serve (defaultpublic).--publicis accepted as an alias for compatibility.--workers <N>— number of worker threads in the pool (default 8).--log-file <path>— append access and internal logs to the provided file.--log-rotate-size <size>— enable size-based rotation; examples:1K,10M,1G.--log-rotate-count <n>— how many rotated files to keep (default 5).--php-cgi <path>— optional: path to a php-cgi executable (or other CGI wrapper) to execute.phpfiles; default isphp-cgi(must be in PATH or an absolute path). When set,.phpfiles under the served directory will be executed via CGI.
The server writes access logs in Combined Log Format when --log-file is set:
%h %l %u [%t] "%r" %>s %b "%{Referer}i" "%{User-agent}i"
Example access line:
127.0.0.1 - - [17/Aug/2025:00:35:10 +0000] "GET /index.html HTTP/1.1" 200 1024 "-" "curl/7.68.0"
This server is intentionally small and educational. Recent hardening above (header, URI, and canonicalization limits) improves resilience, but there are still notable limitations:
- No TLS. Use a reverse proxy (nginx, Caddy) or a TLS-terminating load balancer for production.
- Minimal HTTP parsing: only basic GET is implemented; many headers and edge-cases are not fully RFC-compliant.
- No rate limiting or per-IP connection limits.
- Log rotation is simple rename-based; no compression or time-based rotation.
- No authentication or authorization.
- CGI is implemented by spawning an interpreter process per-request (CreateProcess). This is simple but not high-performance; for production-scale PHP use a FastCGI backend (php-fpm) and a FastCGI client/proxy in front of it.
If you need production-grade behavior, run this behind a hardened proxy or gateway and add tests + CI.
src/server.c- server entrypoint and argument parsingsrc/http.c/src/http.h- HTTP request parsing and static file serving (contains header/URI limits & canonicalization)src/log.c/src/log.h- centralized logging and rotationsrc/threadpool.c/src/threadpool.h- worker queue and thread poolsrc/state.c/src/state.h- shared runtime state (running flag, listen socket)src/utils.c/src/utils.h- small helpers (parse_size, header sanitization)src/cgi.c/src/cgi.h- CGI request handlingMakefile- build helperREADME.md- this file
This repository includes simple helper scripts under the scripts/ directory to make building and starting the server easier on Windows (MinGW/gcc or cmd/PowerShell).
scripts/build.ps1— PowerShell helper: configures (once) with theMinGW Makefilesgenerator and builds theReleasetarget.scripts/build.bat— cmd helper with the same behavior.CMakePresets.json— contains amingwconfigure preset and areleasebuild preset. You can usecmake --preset mingwto configure andcmake --build --preset releaseto build.scripts/start.ps1— PowerShell start script with presets:-WithPhp,-WithLogs,-Port,-ServeDir,-PhpCgiPath,-LogFile.scripts/start.bat— cmd start script. Flags:/phpto enable--php-cgi,/logsto enable--log-file.
# configure/build
.\scripts\build.ps1
# start with php and logging
.\scripts\start.ps1 -WithPhp -WithLogsscripts\build.bat
scripts\start.bat /php /logsThe default start command used by the scripts is equivalent to:
.\build\Cserver.exe --serve "public" --port 8080 --php-cgi "D:\IDE's\php-8.4.11-Win32-vs17-x64\php-cgi.exe" --log-file "server.log"You can override the PHP path, port, and log file via the script parameters.
Small patches, tests, and documentation improvements welcome. If you change public behavior (status codes, limits), please add tests that demonstrate the behavior.
This project uses the MIT License.