HTTP and QUIC Encrypted Ingress
Encrypted DNS ingress over HTTP and QUIC, including DoH, HTTP/2, optional HTTP/3, and DoQ.
http_server
Purpose
Provides DNS over HTTPS and can serve HTTP/1.1, HTTP/2, and optional HTTP/3.
Example Configuration
- tag: doh_in
type: http_server
args:
# HTTPS / DoH bind address
listen: ":443"
# Certificate required for HTTPS and HTTP/3
cert: "/etc/oxidns/server.crt"
# Private key required for HTTPS and HTTP/3
key: "/etc/oxidns/server.key"
# Also enable DoH over HTTP/3 when TLS is present
enable_http3: true
# Restore the real client IP from a reverse-proxy header
src_ip_header: "X-Forwarded-For"
# Idle timeout for HTTP/1.1, HTTP/2, and HTTP/3 connections
idle_timeout: 30
entries:
# Standard RFC 8484 endpoint
- path: "/dns-query"
exec: "seq_main"
# Enable the JSON DNS API for this entry
json_api: true
# Alternate path for a different policy chain
- path: "/dns-alt"
exec: "seq_alt"
Configuration Details
entries
- Type:
array; Required: yes; Default: none - Purpose: Maps HTTP request paths to executors.
- Examples:
path: "/dns-query", exec: "seq_main"path: "/dns-alt", exec: "seq_alt"
- Each item contains:
path- Type:
string - Required: yes
- Purpose: DoH request path.
- Constraint: must start with
/.
- Type:
exec- Type:
string - Required: yes
- Purpose: Executor for requests hitting that path.
- Constraint: must reference an existing executor plugin.
- Type:
json_api- Type:
boolean - Required: no
- Default:
false - Purpose: Enables or disables the JSON DNS API for this path.
- Type:
- Runtime impact:
- Different paths can enter different policy chains.
listen
- Type:
string; Required: yes; Default: none - Purpose: Defines the HTTP or HTTPS bind address.
- Examples:
listen: ":80"listen: ":443"
- Supported forms:
ip:port[ipv6]:port:port
- Runtime impact:
:portmeans dual-stack[::]:port; use0.0.0.0:portfor IPv4-only.
src_ip_header
- Type:
string; Required: no; Default: none - Purpose: Header name used to read the original client address.
- Example:
src_ip_header: "X-Forwarded-For" - Runtime impact:
- Allows a reverse proxy to pass through the real source address.
cert
- Type:
string; Required: no; Default: none - Purpose: HTTPS certificate file path.
- Example:
cert: "/etc/oxidns/server.crt" - Runtime impact:
- HTTPS is enabled only when
certandkeyare both present.
- HTTPS is enabled only when
key
- Type:
string; Required: no; Default: none - Purpose: HTTPS private key file path.
- Example:
key: "/etc/oxidns/server.key" - Runtime impact:
- HTTPS is enabled only when
certandkeyare both present.
- HTTPS is enabled only when
idle_timeout
- Type:
integer; Required: no; Default:30 - Unit: seconds
- Purpose: Controls idle HTTP connection lifetime.
- Example:
idle_timeout: 30 - Runtime impact:
- Affects HTTP/1.1 / HTTP/2 long-lived connection behavior.
enable_http3
- Type:
boolean; Required: no; Default:false - Purpose: Enables HTTP/3 in addition to HTTP/2.
- Example:
enable_http3: true - Requirements:
certandkeymust both be configured.
- Runtime impact:
- Starts an additional QUIC-based DoH listener task.
- HTTP/1.1 / HTTP/2 responses include
Alt-Svc: h3=":<listen-port>"; ma=86400so clients can discover HTTP/3 on the same port.
Behavior
- Each
pathcan route to a differentexec, which is useful for multi-entry policies. - Registers both GET and POST DoH access methods commonly used for RFC 8484.
- With
json_apienabled, GET requests can use JSON API parameters:name(required),type(optional, defaults to1, numeric or text RR type),cd,do,edns_client_subnet, andrandom_padding(accepted and ignored). - If a GET request includes both
dns=andname/type, RFC 8484dns=takes precedence. ct=application/dns-messagereturns DNS wire format; otherwise the JSON API returnsapplication/dns-json.- HTTP/1.1 and HTTP/2 are negotiated automatically on the same listener with no extra configuration.
- When HTTP/3 is enabled, an extra QUIC listener is started.
- When HTTP/3 is enabled, HTTP/1.1 / HTTP/2 responses advertise the same listen port through the
Alt-Svcheader.
Good Fits
- Standard DoH exposure.
- Multiple DNS policy entry points on one listener.
- Deployments behind a reverse proxy that preserve the original client IP with
src_ip_header.
Notes
enable_http3: truerequires bothcertandkey.- If a reverse proxy is involved, define a trusted boundary for
src_ip_headerto avoid spoofed source IPs.
quic_server
Purpose
Provides DNS over QUIC.
Example Configuration
- tag: doq_in
type: quic_server
args:
# Policy chain used by DoQ requests
entry: "seq_main"
# Common DoQ port
listen: ":853"
# TLS certificate is mandatory for DoQ
cert: "/etc/oxidns/server.crt"
# TLS private key is mandatory for DoQ
key: "/etc/oxidns/server.key"
# QUIC transport idle timeout in seconds
idle_timeout: 30
Configuration Details
entry
- Type:
string; Required: yes; Default: none - Purpose: Selects the executor used by DoQ requests.
- Example:
entry: "seq_main" - Requirements:
- Must reference an existing executor plugin.
listen
- Type:
string; Required: yes; Default: none - Purpose: Defines the QUIC bind address.
- Example:
listen: ":853" - Runtime impact:
- Occupies a UDP port.
:portmeans dual-stack[::]:port; use0.0.0.0:portfor IPv4-only.
cert
- Type:
string; Required: yes; Default: none - Purpose: Path to the TLS certificate required by DoQ.
- Example:
cert: "/etc/oxidns/server.crt" - Runtime impact:
- The listener cannot start if the certificate is invalid.
key
- Type:
string; Required: yes; Default: none - Purpose: Path to the TLS private key required by DoQ.
- Example:
key: "/etc/oxidns/server.key" - Runtime impact:
- The listener cannot start if the private key is invalid.
idle_timeout
- Type:
integer; Required: no; Default: none - Unit: seconds
- Purpose: Controls QUIC transport idle timeout.
- Example:
idle_timeout: 30 - Runtime impact:
- Affects when idle QUIC connections are reclaimed.
Behavior
- DoQ always requires TLS, so
certandkeyare mandatory. - ALPN is fixed to
doq. - Each bidirectional stream represents one independent DNS exchange.
Good Fits
- Encrypted DNS ingress with low latency.
- Modern clients that benefit from QUIC transport behavior.
Notes
- The listener still consumes a UDP port underneath.
- Do not bind the same address and port as
udp_server.