Quickstart¶
Install¶
h2corn requires Python 3.11+. Wheels are published for Linux, macOS, and Windows; the multi-worker supervisor is Unix-only — see Operations.
Run your first server¶
Save a tiny FastAPI app as hello.py:
from fastapi import FastAPI
app = FastAPI()
@app.get('/')
async def index():
return {'message': 'hello from h2corn'}
Then start h2corn, pointing it at the module and the ASGI app object inside it:
You'll see something like this in your terminal:
h2corn v1.4.0 • HTTP/2 ASGI
Listening on http://127.0.0.1:8000
HTTP/1 compatibility is enabled; disable with --no-http1
Started worker [12345]
127.0.0.1:54321 "GET / HTTP/1.1" 200 0.4ms tx=25b
Visit http://127.0.0.1:8000/ in your browser. You should see {"message": "hello from h2corn"} come back.
Why does the response say HTTP/1.1?
Browsers do not speak cleartext h2c, so the development server keeps HTTP/1.1 enabled for local testing. In production, the edge advertises HTTPS — either through a reverse proxy or h2corn's own Direct TLS — and you turn HTTP/1.1 off with --no-http1.
Hot reload¶
While iterating locally, add --reload so h2corn restarts whenever your source files change:
The watcher follows *.py by default; tune it with --reload-include and --reload-exclude. Reload is intended for development only and cannot be combined with multiple workers.
Application factories¶
Some applications expose their ASGI object through a factory function rather than a module-level attribute. Pass --factory and h2corn will call it for you:
from fastapi import FastAPI
def create_app() -> FastAPI:
app = FastAPI()
@app.get('/')
async def index():
return {'message': 'hello from h2corn'}
return app
Deploy¶
In production, h2corn typically sits behind a reverse proxy that terminates browser-facing TLS. The application server itself runs on a local listener with several workers, looking something like this:
h2corn hello:app \
--bind 127.0.0.1:8000 \
--workers 4 \
--proxy-headers \
--forwarded-allow-ips 127.0.0.1,::1,unix \
--no-http1
Each flag above makes a deliberate choice worth understanding before you ship it:
--bind 127.0.0.1:8000listens on loopback only so the proxy can reach it locally. Use0.0.0.0:portif you want a public-facing TCP listener instead.--workers 4is a reasonable starting point on a 4-core box. Size it to your workload and revisit after measuring.--proxy-headerstrusts standardForwardedandX-Forwarded-*headers, but only when they come from the peers listed in--forwarded-allow-ips. Set that list to wherever your proxy actually connects from.--no-http1is a fail-closed hardening flag. Once the upstream is configured to speakh2c, an accidental fallback fails immediately instead of quietly serving traffic on the older protocol.
Once you have more than a handful of flags, prefer a TOML config file — same keys, single source of truth, easier to review in a pull request.
Next steps¶
- Behind a proxy — full Caddy and HAProxy recipes with
h2cupstream. - Direct TLS — terminate TLS in
h2cornitself for single-server deployments. - Operations — multi-worker supervisor, signals, rolling reload, live scaling, and recycling.
- Configuration — every option, in CLI, environment, and TOML form.