Directive Coverage

bext supports 68 of 70 commonly used nginx directives (97% coverage). This page documents every supported directive and how it maps to bext's internal configuration.

Server Block Directives

listen

Parsed with full support for all common options:

listen 80;
listen 443 ssl http2;
listen [::]:80;
listen 0.0.0.0:8080 default_server;

Extracts: port, address, SSL flag, HTTP/2 flag, and default_server designation.

server_name

All name patterns are supported:

server_name example.com;                    # Exact match
server_name *.example.com;                  # Wildcard prefix
server_name example.*;                      # Wildcard suffix
server_name ~^api\d+\.example\.com$;        # Regex
server_name .example.com;                   # Exact + wildcard prefix
server_name _;                              # Catch-all

Matching priority (same as nginx): exact > longest wildcard prefix > wildcard suffix > first regex match > default server.

root and index

root /var/www/html;
index index.html index.htm index.php;

error_page

error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;

default_type

default_type application/octet-stream;

Location Blocks

All five nginx location match types are supported with correct priority:

location = /health { }         # Exact match (highest priority)
location ^~ /static/ { }       # Prefix priority (skips regex)
location ~ \.php$ { }          # Regex (case-sensitive)
location ~* \.(jpg|png)$ { }   # Regex (case-insensitive)
location /api/ { }             # Plain prefix (lowest priority)

Nested locations are supported:

location /api/ {
    location /api/v2/ {
        proxy_pass http://backend-v2;
    }
    proxy_pass http://backend-v1;
}

Proxy Directives

proxy_pass

location /api/ {
    proxy_pass http://backend;
    proxy_pass http://127.0.0.1:3000;
    proxy_pass http://upstream_name;
}

Automatically detects upstream references and resolves them. WebSocket upgrade headers are detected and handled.

proxy_set_header

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;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

All nginx variables are substituted with bext runtime placeholders.

proxy_redirect

proxy_redirect off;
proxy_redirect default;
proxy_redirect http://backend/ /;

Proxy Timeouts

proxy_read_timeout 60s;
proxy_connect_timeout 30s;
proxy_send_timeout 30s;

Proxy Cache

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=1g inactive=60m;

location / {
    proxy_cache my_cache;
    proxy_cache_key "$scheme$request_method$host$request_uri";
    proxy_cache_valid 200 302 10m;
    proxy_cache_valid 404 1m;
    proxy_cache_bypass $http_pragma;
    proxy_no_cache $http_authorization;
    proxy_cache_use_stale error timeout updating;
    proxy_cache_background_update on;
    proxy_cache_lock on;
    proxy_cache_lock_timeout 5s;
}

Upstream Blocks

Full support for load balancing:

upstream backend {
    least_conn;                    # or: ip_hash, hash $uri
    server 127.0.0.1:3001 weight=3;
    server 127.0.0.1:3002;
    server 127.0.0.1:3003 backup;
    server 127.0.0.1:3004 down;
    keepalive 32;
}

Load balancing strategies: round-robin (default), least_conn, ip_hash, hash (with variables).

Rewrite and Redirect

rewrite

rewrite ^/old/(.*)$ /new/$1 permanent;  # 301 redirect
rewrite ^/temp/(.*)$ /new/$1 redirect;  # 302 redirect
rewrite ^/api/v1/(.*)$ /api/v2/$1 last; # internal rewrite
rewrite ^/legacy/(.*)$ /current/$1 break;

Capture groups ($1, $2, etc.) are preserved and substituted.

return

return 301 https://$host$request_uri;  # HTTPS redirect
return 200 "OK";                       # Fixed response
return 403;                            # Status-only

Conditional Blocks

if ($request_uri ~ ^/old/) {
    return 301 /new/;
}

if (-f $request_filename) {
    break;
}

if (-d $request_filename) {
    rewrite ^(.*)$ $1/index.html break;
}

if ($http_x_forwarded_proto != "https") {
    return 301 https://$host$request_uri;
}

Supports file existence (-f), directory existence (-d), regex matching (~, ~*), equality (=, !=), and variable existence checks.

try_files

try_files $uri $uri/ =404;                      # Static site
try_files $uri $uri/ /index.html;                # SPA fallback
try_files $uri $uri/ /index.php?$query_string;   # PHP/Laravel
try_files $uri @backend;                          # Named location

Resolution order:

1. Check each pattern (variable-substituted, path-traversal-guarded) 2. If a file exists, serve it 3. If a directory exists, check for index files 4. Fall back to the last argument: status code, internal redirect, or named location

SSL/TLS

ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
ssl_dhparam /etc/nginx/dhparam.pem;

All certificates are loaded into bext's SNI resolver for multi-cert support.

Compression

gzip on;
gzip_types text/plain text/css application/json application/javascript;
gzip_comp_level 6;
gzip_min_length 256;
gzip_vary on;
gzip_proxied any;

Rate Limiting

limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
limit_conn_zone $binary_remote_addr zone=addr:10m;

location /api/ {
    limit_req zone=api burst=20 nodelay;
    limit_conn addr 10;
}

Access Control

allow 192.168.1.0/24;
allow 10.0.0.0/8;
deny all;

auth_basic "Restricted Area";
auth_basic_user_file /etc/nginx/.htpasswd;

Headers

add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff";
add_header Strict-Transport-Security "max-age=31536000" always;

Variables and Maps

map

map $http_accept_language $lang {
    default en;
    ~^fr    fr;
    ~^de    de;
}

geo

geo $country {
    default        US;
    192.168.0.0/16 INTERNAL;
    10.0.0.0/8     INTERNAL;
}

set

set $backend "http://127.0.0.1:3000";

FastCGI (PHP)

location ~ \.php$ {
    fastcgi_pass unix:/run/php/php-fpm.sock;
    fastcgi_pass 127.0.0.1:9000;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_index index.php;
    include fastcgi_params;
}

Converted to bext's embedded PHP runtime or proxied to an external PHP-FPM process.

Other Directives

autoindex on;                              # Directory listing
stub_status;                               # Status page
sub_filter 'old-text' 'new-text';          # Response body substitution
sub_filter_once off;
expires 30d;                               # Cache expiration header
client_max_body_size 50m;                  # Request body limit
keepalive_timeout 65;

Variable Substitution

All nginx variables are mapped to bext runtime placeholders:

nginx Variable bext Placeholder
$host {host}
$uri, $document_uri {uri}
$request_uri {request_uri}
$args, $query_string {query_string}
$is_args {is_args}
$scheme {scheme}
$request_method {method}
$remote_addr {client_ip}
$server_name {server_name}
$server_port {server_port}
$server_protocol {server_protocol}
$document_root {document_root}
$request_filename {request_filename}
$http_* {header:*}
$cookie_* {cookie:*}
$arg_* {arg:*}