Configuration language
Bring your nginx config. Xin reads the same block-and-directive language, including includes, variables, inheritance and nginx's location-selection rules.
Configuration file structure
A xin configuration is a tree of directives. A simple directive ends with a semicolon. A block directive contains more directives between braces. The root of the file is the main context.
worker_processes auto;
events {
worker_connections 4096;
}
http {
include /etc/nginx/mime.types;
server {
listen 8080;
location / { return 200 "hello\n"; }
}
} | Context | Purpose | Common children |
|---|---|---|
main | Process-wide configuration | events, http, stream |
events | Connection-worker settings | worker_connections |
http | Defaults for HTTP servers | server, upstream, map, geo |
server | A virtual HTTP server | listen, server_name, location |
location | URI matching and request handling | content, proxy, access, limit and filter directives |
upstream | A named backend pool | server, load-balancing policy, keepalive |
stream | TCP proxying | server, upstream, map |
Tokens, comments and quoting
Spaces, tabs and newlines separate tokens. A # outside quotes starts a comment through the end of the line. The characters ;, { and } terminate directives or blocks when they are not quoted or escaped.
Single and double quotes keep whitespace inside one argument. A backslash escapes the following character. Quote an argument when it contains whitespace, a semicolon, braces, a leading #, or a literal dollar sign that must not begin a variable.
# one argument after the status code
return 200 "hello from xin
";
# a literal dollar sign
add_header X-Price "\$10"; Includes and the configuration prefix
include accepts a file path or glob. Absolute paths are used as written. Relative paths are resolved from the active configuration prefix. The Public Beta parses nginx's -p CLI option for command-line compatibility but does not yet apply it. Glob matches are read in lexical order. Includes can contain more includes; cycles are rejected with the full chain.
http {
include /etc/nginx/mime.types;
include /etc/nginx/conf.d/*.conf;
} Contexts and inheritance
Every directive has a fixed set of valid contexts. Using one elsewhere is a configuration error. Values flow from broader contexts into narrower ones: http → server → location → nested location or if.
Most scalar directives inherit until a child defines its own value. Lists usually inherit as a whole: the first directive at a child level replaces the complete parent list. This matters for directives such as proxy_set_header, add_header and access rules—repeat every entry the child needs.
http {
proxy_set_header Host $host;
proxy_set_header X-Request-ID $request_id;
server {
# Defining one proxy_set_header here replaces the inherited set.
proxy_set_header Host internal.example;
}
} Sizes, times and rates
Size arguments accept bytes or a case-insensitive k, m or g suffix. Time arguments accept milliseconds (ms), seconds (s), minutes (m), hours (h), days (d) and weeks (w); a bare time value is seconds where nginx defines it that way. Rate directives accept the units named on their reference page.
client_max_body_size 20m;
proxy_connect_timeout 3s;
proxy_read_timeout 1m;
limit_rate 512k; Variables
Variables begin with $ and are evaluated for each request. Use $name or ${name} when text immediately follows the name. Built-ins expose the request, selected route, connection, TLS state and upstream result. Header variables use $http_ for request headers, $sent_http_ for response headers and $upstream_http_ for upstream response headers; header names are lowercased and dashes become underscores.
set assigns a value in rewrite processing. map, geo and split_clients declare lazily evaluated variables at http scope. Regular-expression captures are available as $1 through $9 and by named capture.
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade; How a virtual server is selected
Xin first selects the listening address and port. The default_server for that socket handles the request unless server_name produces a better match. Name matching uses nginx order: exact name, longest leading wildcard, longest trailing wildcard, then regular expressions in configuration order. Names are compared case-insensitively after host normalization.
How a location is selected
- An exact
location = /pathmatch wins immediately. - Xin remembers the longest matching prefix location, descending through nested prefix locations.
- If the remembered prefix uses
^~, it wins without testing regular expressions. - Regular-expression locations are tested in configuration order. The first match wins.
- If no regular expression matches, the longest remembered prefix wins.
Named locations such as @app are not selected from the request URI. They are targets for internal redirects from directives including try_files, error_page and upstream retry handling.
URIs, arguments and internal redirects
Location matching uses the normalized URI path, not the query string. $request_uri keeps the original path and arguments; $uri is the current normalized URI and can change during internal redirects. $args contains the current query string.
rewrite ... last, try_files, index, error_page and X-Accel-Redirect can restart URI and location processing internally. Xin bounds redirect loops and reports the failure instead of recursing indefinitely.
Filesystem paths: root, alias and try_files
root appends the URI to a directory. alias replaces the matched location prefix. try_files checks candidates using the active root or alias; only its final argument is a URI, named location or status fallback.
location /assets/ {
root /srv/www; # /assets/app.css -> /srv/www/assets/app.css
}
location /downloads/ {
alias /srv/files/; # /downloads/a.zip -> /srv/files/a.zip
}
location / {
try_files $uri $uri/ @application;
} Reverse proxying and URI replacement
When proxy_pass names an upstream without a URI, the current request URI is forwarded. When it includes a URI, the part matched by the normalized location is replaced. A variable in proxy_pass changes these rules; consult the directive page before using a dynamic target.
location /api/ {
proxy_pass http://backend/; # /api/users -> /users
}
location /raw/ {
proxy_pass http://backend; # /raw/users -> /raw/users
} Request headers are controlled with proxy_set_header; an empty value suppresses a header. Buffering, request buffering, connect/send/read timeouts, retry policy, upstream TLS verification, response-header handling and cookie rewriting are independently configurable.
Upstream groups
An upstream block gives a backend pool a name. Servers can have weights, passive failure thresholds, failure windows, backup status and administrative down status. The default policy is weighted round robin; least_conn, ip_hash, hash and random select alternatives. keepalive retains idle upstream connections per worker.
upstream application {
least_conn;
server 10.0.0.8:8080 weight=2 max_fails=3 fail_timeout=10s;
server 10.0.0.9:8080;
server 10.0.0.10:8080 backup;
keepalive 32;
} TLS
Enable TLS with listen ... ssl, then provide ssl_certificate and ssl_certificate_key. Multiple certificate pairs support RSA and ECDSA selection. ssl_protocols, explicit cipher suites, client certificates, session caches, tickets, OCSP stapling and ALPN are configurable.
The default package uses rustls with AWS-LC, a cryptographic library derived from BoringSSL. Xin does not claim that its entire TLS stack is written in memory-safe Rust. The request and configuration engines enforce their own safe-code boundaries; the memory-safety page documents the boundary precisely.
Content handlers and filters
A location normally chooses one primary content handler: static files, return, HTTP proxy, FastCGI, uwsgi, SCGI, gRPC, autoindex, empty GIF or another compiled feature. Response filters then apply headers, ranges, conditional requests, substitutions, addition bodies and gzip according to their status and content-type rules.
Access, authentication and limits
allow/deny, HTTP Basic auth, subrequest auth, secure links and referer checks run in the access phase. satisfy any or satisfy all combines access modules. Request-rate and connection limits use shared zones declared at http scope and applied in a server or location. Body size, header/body timeouts, response rate and keepalive limits are separate controls.
Logging
log_format declares a format at http scope. access_log selects the path and format and can be disabled or made conditional. Variables are rendered after request processing so status, bytes and upstream timing are available. error_log selects the destination and minimum severity.
Stream TCP configuration
The stream context configures TCP listeners and upstream pools independently from HTTP. It supports address-based access control, proxying, load balancing, timeouts, logging, PROXY protocol and TLS ClientHello inspection with ssl_preread. UDP and the nginx mail module are not part of the Public Beta.
Validation and unsupported directives
Unknown directives, invalid contexts, wrong argument counts, invalid values and feature-gated directives fail configuration loading with a source file and line number. Xin does not silently accept a directive it cannot apply. See configuration compatibility for the current supported families, refused modules and known beta differences.
Testing before running
xin -t -c /etc/nginx/nginx.conf
xin -T -c /etc/nginx/nginx.conf -t parses and validates without starting listeners. -T also prints every resolved configuration file. Configuration validation cannot exercise every request-time branch, so run representative traffic on a spare port before switching a production listener.