Development Web Server
webdev is a small, fast local development web server. Point it at a folder and it serves it over HTTP (or HTTPS) with developer-friendly conveniences and a modern, dark-themed directory index.
It is built on Starlette and Uvicorn (an async ASGI stack) and requires Python 3.10+.
Quick start
# Install (see more options below) uv tool install webdev # Serve the current directory on http://localhost:8080 webdev # Serve a specific folder webdev ./public # See every option webdev --help
Installation
With uv (recommended):
uv tool install webdev
With pipx:
pipx install webdev
Or with plain pip into an environment of your choice:
pip install webdev
Features
🗂️ Serve any directory — passed as the first positional argument; defaults to the current directory.
🩹 Graceful when the folder disappears — responds with 404 instead of crashing if the served directory is removed, and recovers automatically the moment it is recreated. Paths are resolved against the live filesystem on every request — ideal for build systems that clean and rebuild output.
🔀 Automatic port selection — if the base port is busy, the next free port in a range is used. Can be forced to a single exact port.
🔮 Serve a not-yet-existing folder — --future assumes the folder will be created later.
🎨 Modern dark directory index — breadcrumbs, file-type icons, human-readable sizes, modified dates and client-side sortable columns. Themable: drop a new directory under webdev/templates/<theme>/.
🔁 Live reload — connected browsers refresh automatically on file changes (via a WebSocket; a tiny auto-reconnecting snippet is injected into HTML).
📱 Instant mobile testing — a QR code and LAN URL are printed at startup so you can open the site on your phone.
🌈 Colorized, level-aware logs for readable output.
⚡ No-cache headers for always-fresh development content.
👀 Previews text inline — plain-text, config and template files (.txt, .json, .yaml, .ini, .toml, .j2/.jinja2, .template, .config and more) open in the browser instead of being downloaded.
🧭 SPA fallback — optionally serve index.html for unknown routes.
📦 Gzip, CORS and optional HTTPS (self-signed if no cert/key is given).
⚙️ Config file — every option can be set via webdev.toml (or a [tool.webdev] table). Precedence: CLI flag > config file > default.
Usage
$ webdev --help
usage: webdev [-h] [-v] [--version] [-i IP] [-p PORT] [-f] [-r RANGE] [-u]
[--reload | --no-reload] [--cache] [--no-cache]
[--spa | --no-spa] [--gzip | --no-gzip] [--cors | --no-cors]
[--qr | --no-qr] [--color | --no-color]
[--https | --no-https] [--cert CERT]
[--key KEY] [--theme THEME] [--config CONFIG]
[path]
Examples:
# Serve the current directory on http://localhost:8080 webdev # Serve ./public on a forced port, over HTTPS, without live reload webdev -p 3000 -f --https --no-reload ./public # Single-page app: fall back to index.html for client-side routes webdev --spa ./dist # Serve a build output folder that does not exist yet webdev --future ./dist
Options and config keys
Every option has both a CLI flag and a config-file key. Booleans accept --x / --no-x.
Feature |
CLI flag |
Config key |
Default |
|---|---|---|---|
Serve path |
positional path |
path |
CWD |
Bind IP |
-i/--ip |
ip |
0.0.0.0 |
Base port |
-p/--port |
port |
8080 |
Force exact port |
-f/--force-port |
force_port |
false |
Port range |
-r/--range |
range |
20 |
Serve missing folder |
-u/--future |
future |
false |
No-cache headers |
--no-cache/--cache |
no_cache |
enabled |
Live reload |
--reload/--no-reload |
reload |
enabled |
SPA fallback |
--spa/--no-spa |
spa |
disabled |
Gzip |
--gzip/--no-gzip |
gzip |
enabled |
CORS |
--cors/--no-cors |
cors |
disabled |
QR / LAN URL |
--qr/--no-qr |
qr |
enabled |
Colorized logs |
--color/--no-color |
color |
enabled |
HTTPS/TLS |
--https/--no-https |
https |
disabled |
TLS cert / key |
--cert / --key |
cert / key |
self-signed |
Index theme |
--theme |
theme |
dark |
Verbosity |
-v (repeatable) |
verbose |
0 |
Commit a webdev.toml next to your project to share settings with your team. Any option works as a flat key, or under a [tool.webdev] table (handy inside a pyproject.toml):
# webdev.toml port = 3000 force_port = true spa = true cors = true
Development
The development workflow is driven by tox using uv as the backend (via tox-uv). uv is the only tool you need preinstalled:
# Lint, format-check and type-check tox -e style # Run the test suite tox -e test # Build and verify the distribution tox -e build # Build, install in a throwaway venv and run (pass args after --) tox -e run -- -p 3000 ./public
Each environment declares its own tooling dependencies in tox.ini so it installs only what it needs.
Changelog
2.1.0
New
Plain-text, config and template files (such as .yaml, .tpl, .j2, .jinja2, .template and .config) now open inline in the browser instead of being downloaded (issue #8). Thanks to zaro0508 for the report.
2.0.1
Changed
Modernized and expanded the README (uv-first installation, refreshed feature overview and usage).
2.0.0
Reimplemented on a modern async stack (Starlette + Uvicorn), Python 3.10+.
New
Modern dark, themable directory index (breadcrumbs, icons, sizes, dates, sortable columns), rendered with Jinja2.
Live reload over WebSocket.
No-cache headers, SPA fallback, gzip, CORS, optional HTTPS (self-signed), startup QR code / LAN URL.
Colorized, level-aware log output (--color/--no-color, via colorlog).
Config-file support (webdev.toml / [tool.webdev]); every option has a CLI flag and a config key (precedence CLI > config > default).
pyproject.toml packaging; tox + uv development workflow; Ruff, mypy and a pytest suite.
Fixed
The directory auto-recovery feature, which silently broke on modern Python when http.server began capturing the working directory in the request handler constructor. Path resolution is now per-request against the live filesystem.
Changed
Dropped Python 2 support.
1.1.0
New
Added support for Python 3 (issue #5).
Added support for incremental automatic port selection (issue #2).
Added the --future flag that allows the webserver to serve a non-existent folder, assuming it will be created in the future (issue #3).
Changed
Ctrl+C is now handled (issue #4).
1.0.0
Initial release.
License
Copyright (C) 2026 KuraLabs S.R.L <info@kuralabs.io>
Copyright (C) 2015-2016 Carlos Jenkins <carlos@jenkins.co.cr>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
Metadata
Release files for webdev 2.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| webdev-2.1.0.tar.gz | 34.5 kB | Without provenance | Details for webdev-2.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| webdev-2.1.0-py3-none-any.whl | Python 3 | none | any | Details for webdev-2.1.0-py3-none-any.whl |
Total release size: 74.6 kB
Release files / webdev-2.1.0.tar.gz
| Download URL | webdev-2.1.0.tar.gz |
|---|---|
| Size | 34.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
bf5375826dcf02310588be74a0776322b6bd1ad952ede36b9532e19540ab387b
|
|
BLAKE2b-256 checksum How to use checksums |
c855cbedd761b01836f0d9e7ff8dee7c859b84011788070dfa6616e6a972ba59
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.7
|
Release files / webdev-2.1.0-py3-none-any.whl
| Download URL | webdev-2.1.0-py3-none-any.whl |
|---|---|
| Size | 40.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
43080e0b8bf8d0d9160e6c0d91052359d50b2d8e73e99a4b3827c13514cdca80
|
|
BLAKE2b-256 checksum How to use checksums |
968a3f694e1dfda63b97ddeb0a69a131500ab4cd69a8b500f2bedb75191277d0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.7
|
Security for webdev 2.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.