diff --git a/docs/content/2026-news.md b/docs/content/2026-news.md index a749137e..7f0d4e90 100644 --- a/docs/content/2026-news.md +++ b/docs/content/2026-news.md @@ -16,6 +16,24 @@ - Lifecycle hooks: `on_dirty_starting`, `dirty_post_fork`, `dirty_worker_init`, `dirty_worker_exit` +- **HTTP/2 Support (Beta)**: Native HTTP/2 (RFC 7540) support for improved performance + with modern clients ([PR #3462](https://github.com/benoitc/gunicorn/pull/3462)) + - Multiplexed streams over a single connection + - Header compression (HPACK) + - Flow control and stream prioritization + - Works with gthread, gevent, eventlet, and ASGI workers + - New settings: `--http-protocols`, `--http2-max-concurrent-streams`, + `--http2-initial-window-size`, `--http2-max-frame-size`, `--http2-max-header-list-size` + - Requires SSL/TLS and h2 library: `pip install gunicorn[http2]` + - See [HTTP/2 Guide](guides/http2.md) for details + +- **HTTP 103 Early Hints**: Support for RFC 8297 Early Hints to enable browsers to + preload resources before the final response + ([PR #3462](https://github.com/benoitc/gunicorn/pull/3462)) + - WSGI: `environ['wsgi.early_hints'](headers)` callback + - ASGI: `http.response.informational` message type + - Works with both HTTP/1.1 and HTTP/2 + --- ## 24.1.1 - 2026-01-24 diff --git a/docs/content/asgi.md b/docs/content/asgi.md index db1a6fcd..d66e5818 100644 --- a/docs/content/asgi.md +++ b/docs/content/asgi.md @@ -235,11 +235,14 @@ asgi_lifespan = "auto" # Auto-detect lifespan support | Feature | Gunicorn ASGI | Uvicorn | Hypercorn | |---------|---------------|---------|-----------| | Process management | Built-in | External | Built-in | -| HTTP/2 | No | No | Yes | +| HTTP/2 | Yes | No | Yes | | WebSocket | Yes | Yes | Yes | | Lifespan | Yes | Yes | Yes | | uvloop support | Yes | Yes | Yes | +!!! note + HTTP/2 requires SSL/TLS and the h2 library. See [HTTP/2 Support](http2.md) for details. + Gunicorn's ASGI worker provides the same process management, logging, and configuration capabilities you're familiar with from WSGI deployments. diff --git a/docs/content/guides/http2.md b/docs/content/guides/http2.md new file mode 100644 index 00000000..902d0d0c --- /dev/null +++ b/docs/content/guides/http2.md @@ -0,0 +1,336 @@ +# HTTP/2 Support + +!!! warning "Beta Feature" + HTTP/2 support is a beta feature introduced in Gunicorn 25.0.0. While it has been tested, + the API and behavior may change in future releases. Please report any issues on + [GitHub](https://github.com/benoitc/gunicorn/issues). + +Gunicorn supports HTTP/2 (RFC 7540) for improved performance with modern clients. +HTTP/2 provides multiplexed streams, header compression, and other optimizations +over HTTP/1.1. + +## Quick Start + +```bash +# Install gunicorn with HTTP/2 support +pip install gunicorn[http2] + +# Run with HTTP/2 enabled (requires SSL) +gunicorn myapp:app \ + --worker-class gthread \ + --threads 4 \ + --certfile server.crt \ + --keyfile server.key \ + --http-protocols h2,h1 +``` + +## Requirements + +HTTP/2 support requires: + +- **SSL/TLS**: HTTP/2 uses ALPN (Application-Layer Protocol Negotiation) which + requires an encrypted connection +- **h2 library**: Install with `pip install gunicorn[http2]` or `pip install h2` +- **Compatible worker**: gthread, gevent, eventlet, or ASGI workers + +## Configuration + +### Enable HTTP/2 + +Enable HTTP/2 by setting the `--http-protocols` option: + +```bash +gunicorn myapp:app --http-protocols h2,h1 +``` + +Or in a configuration file: + +```python +# gunicorn.conf.py +http_protocols = ["h2", "h1"] +``` + +The order matters for ALPN negotiation - protocols are tried in order of preference. + +| Protocol | Description | +|----------|-------------| +| `h2` | HTTP/2 over TLS | +| `h1` | HTTP/1.1 (fallback) | + +!!! note + Always include `h1` as a fallback for clients that don't support HTTP/2. + +### SSL/TLS Configuration + +HTTP/2 requires SSL/TLS. Configure certificates: + +```bash +gunicorn myapp:app \ + --certfile /path/to/server.crt \ + --keyfile /path/to/server.key \ + --http-protocols h2,h1 +``` + +Or in a configuration file: + +```python +# gunicorn.conf.py +certfile = "/path/to/server.crt" +keyfile = "/path/to/server.key" +http_protocols = ["h2", "h1"] +``` + +### HTTP/2 Settings + +Fine-tune HTTP/2 behavior with these settings: + +| Setting | Default | Description | +|---------|---------|-------------| +| `http2_max_concurrent_streams` | 100 | Maximum concurrent streams per connection | +| `http2_initial_window_size` | 65535 | Initial flow control window size (bytes) | +| `http2_max_frame_size` | 16384 | Maximum frame size (bytes) | +| `http2_max_header_list_size` | 65536 | Maximum header list size (bytes) | + +Example configuration: + +```python +# gunicorn.conf.py +http_protocols = ["h2", "h1"] +http2_max_concurrent_streams = 200 +http2_initial_window_size = 1048576 # 1MB +``` + +## Worker Compatibility + +Not all workers support HTTP/2: + +| Worker | HTTP/2 Support | Notes | +|--------|----------------|-------| +| `sync` | No | Single-threaded, cannot multiplex streams | +| `gthread` | Yes | Recommended for HTTP/2 | +| `gevent` | Yes | Requires gevent | +| `eventlet` | Yes | Requires eventlet | +| `asgi` | Yes | For async frameworks | +| `tornado` | No | Tornado handles its own protocol | + +If you use the sync or tornado worker with HTTP/2 enabled, Gunicorn will log a +warning and fall back to HTTP/1.1. + +### Recommended: gthread Worker + +For HTTP/2, the gthread worker is recommended: + +```bash +gunicorn myapp:app \ + --worker-class gthread \ + --threads 4 \ + --workers 2 \ + --http-protocols h2,h1 \ + --certfile server.crt \ + --keyfile server.key +``` + +## HTTP 103 Early Hints + +Gunicorn supports HTTP 103 Early Hints (RFC 8297), allowing servers to send +resource hints before the final response. This enables browsers to preload +CSS, JavaScript, and other assets in parallel. + +### WSGI Applications + +Use the `wsgi.early_hints` callback in your WSGI application: + +```python +def app(environ, start_response): + # Send early hints if available + if 'wsgi.early_hints' in environ: + environ['wsgi.early_hints']([ + ('Link', '; rel=preload; as=style'), + ('Link', '; rel=preload; as=script'), + ]) + + # Continue with the actual response + start_response('200 OK', [('Content-Type', 'text/html')]) + return [b'...'] +``` + +### ASGI Applications + +Use the `http.response.informational` message type: + +```python +async def app(scope, receive, send): + # Send early hints + await send({ + "type": "http.response.informational", + "status": 103, + "headers": [ + (b"link", b"; rel=preload; as=style"), + (b"link", b"; rel=preload; as=script"), + ], + }) + + # Send the actual response + await send({ + "type": "http.response.start", + "status": 200, + "headers": [(b"content-type", b"text/html")], + }) + await send({ + "type": "http.response.body", + "body": b"...", + }) +``` + +!!! note + Early hints are only sent to HTTP/1.1+ clients. HTTP/1.0 clients silently + ignore the callback since they don't support 1xx responses. + +## Production Deployment + +### With Nginx + +Configure nginx to proxy HTTP/2 connections to Gunicorn: + +```nginx +upstream gunicorn { + server 127.0.0.1:8443; + keepalive 32; +} + +server { + listen 443 ssl; + http2 on; + server_name example.com; + + ssl_certificate /path/to/server.crt; + ssl_certificate_key /path/to/server.key; + ssl_protocols TLSv1.2 TLSv1.3; + + # Forward 103 Early Hints (requires nginx 1.29+) + location / { + proxy_pass https://gunicorn; + proxy_http_version 1.1; + proxy_ssl_verify off; + + early_hints $http2; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} +``` + +!!! note + For nginx to forward 103 Early Hints from upstream, you need nginx 1.29+ + and the `early_hints` directive. + +### Direct TLS Termination + +For simpler deployments, Gunicorn can terminate TLS directly: + +```python +# gunicorn.conf.py +bind = "0.0.0.0:443" +worker_class = "gthread" +threads = 4 +workers = 4 + +# SSL +certfile = "/etc/letsencrypt/live/example.com/fullchain.pem" +keyfile = "/etc/letsencrypt/live/example.com/privkey.pem" + +# HTTP/2 +http_protocols = ["h2", "h1"] +http2_max_concurrent_streams = 100 +``` + +### Recommended Settings + +For production HTTP/2 deployments: + +```python +# gunicorn.conf.py +worker_class = "gthread" +workers = 4 +threads = 4 +keepalive = 120 # HTTP/2 connections are long-lived + +# SSL/TLS +certfile = "/path/to/server.crt" +keyfile = "/path/to/server.key" +ssl_version = "TLSv1_2" # Minimum TLS 1.2 for HTTP/2 + +# HTTP/2 +http_protocols = ["h2", "h1"] +http2_max_concurrent_streams = 100 +http2_initial_window_size = 65535 +``` + +## Troubleshooting + +### HTTP/2 not negotiated + +If clients fall back to HTTP/1.1: + +1. Verify SSL is configured correctly +2. Check that `h2` is in `--http-protocols` +3. Ensure the h2 library is installed: `pip install h2` +4. Verify ALPN support: `openssl s_client -alpn h2 -connect host:port` + +### Worker doesn't support HTTP/2 + +If you see "HTTP/2 is not supported by the sync worker": + +```bash +# Switch to gthread worker +gunicorn myapp:app --worker-class gthread --threads 4 +``` + +### Connection errors with large requests + +Increase flow control window sizes: + +```python +http2_initial_window_size = 1048576 # 1MB +http2_max_frame_size = 32768 # 32KB +``` + +### Too many concurrent streams + +If clients report stream limit errors: + +```python +http2_max_concurrent_streams = 200 # Increase from default 100 +``` + +## Testing HTTP/2 + +### Using curl + +```bash +# Check HTTP/2 support +curl -v --http2 https://localhost:8443/ + +# Force HTTP/2 +curl --http2-prior-knowledge https://localhost:8443/ +``` + +### Using Python + +```python +import httpx + +with httpx.Client(http2=True, verify=False) as client: + response = client.get("https://localhost:8443/") + print(f"HTTP Version: {response.http_version}") +``` + +## See Also + +- [Settings Reference](reference/settings.md#http2_max_concurrent_streams) - All HTTP/2 settings +- [ASGI Worker](asgi.md) - ASGI worker with HTTP/2 support +- [Deploy](deploy.md) - General deployment guidance +- [SSL Configuration](deploy.md#using-ssl) - SSL/TLS setup diff --git a/docs/content/reference/settings.md b/docs/content/reference/settings.md index 027cb1f9..529dfbed 100644 --- a/docs/content/reference/settings.md +++ b/docs/content/reference/settings.md @@ -287,6 +287,111 @@ are force killed. !!! info "Added in 25.0.0" +## HTTP/2 + +### `http_protocols` + +**Command line:** `--http-protocols STRING` + +**Default:** `'h1'` + +HTTP protocol versions to support (comma-separated, order = preference). + +Valid protocols: + +* ``h1`` - HTTP/1.1 (default) +* ``h2`` - HTTP/2 (requires TLS with ALPN) +* ``h3`` - HTTP/3 (future, not yet implemented) + +Examples:: + + # HTTP/1.1 only (default, backward compatible) + --http-protocols=h1 + + # Prefer HTTP/2, fallback to HTTP/1.1 + --http-protocols=h2,h1 + + # HTTP/2 only (reject HTTP/1.1 clients) + --http-protocols=h2 + +HTTP/2 requires: + +* TLS (--certfile and --keyfile) +* The h2 library: ``pip install gunicorn[http2]`` +* ALPN-capable TLS client + +!!! note + HTTP/2 cleartext (h2c) is not supported due to security concerns + and lack of browser support. + +!!! info "Added in 25.0.0" + +### `http2_max_concurrent_streams` + +**Command line:** `--http2-max-concurrent-streams INT` + +**Default:** `100` + +Maximum number of concurrent HTTP/2 streams per connection. + +This limits how many requests can be processed simultaneously on a +single HTTP/2 connection. Higher values allow more parallelism but +use more memory. + +Default is 100, which matches common server configurations. +The HTTP/2 specification allows up to 2^31-1. + +!!! info "Added in 25.0.0" + +### `http2_initial_window_size` + +**Command line:** `--http2-initial-window-size INT` + +**Default:** `65535` + +Initial HTTP/2 flow control window size in bytes. + +This controls how much data can be in-flight before the receiver +sends WINDOW_UPDATE frames. Larger values can improve throughput +for large transfers but use more memory. + +Default is 65535 (64KB - 1), the HTTP/2 specification default. +Maximum is 2^31-1 (2147483647). + +!!! info "Added in 25.0.0" + +### `http2_max_frame_size` + +**Command line:** `--http2-max-frame-size INT` + +**Default:** `16384` + +Maximum HTTP/2 frame payload size in bytes. + +This is the largest frame payload the server will accept. +Larger frames reduce framing overhead but may increase latency +for small messages. + +Default is 16384 (16KB), the HTTP/2 specification minimum. +Range is 16384 to 16777215 (16MB - 1). + +!!! info "Added in 25.0.0" + +### `http2_max_header_list_size` + +**Command line:** `--http2-max-header-list-size INT` + +**Default:** `65536` + +Maximum size of HTTP/2 header list in bytes (HPACK protection). + +This limits the total size of headers after HPACK decompression. +Protects against compression bombs and excessive memory use. + +Default is 65536 (64KB). Set to 0 for unlimited (not recommended). + +!!! info "Added in 25.0.0" + ## Logging ### `accesslog`