WBS Server Reference Manual#
2026-04
Table of contents#
Introduction#
One of the fundamental units of the internet is the web server. These servers are computers built to deliver the requested webpage. Every web server has an IP address and a domain name. To make your computer a web server, you have to install web server software, such as WBS.
WBS is closed-source web server software used for sharing content and reverse proxy. It provides HTTPS server capabilities and is mainly designed for maximum performance and stability. It also functions as a proxy server for the web socket protocol.
Use Cases#
An instance of WBS can be configured as any of the following:
-
A web server. This is the most common because of its performance and scalability.
-
A reverse proxy server. WBS does this by directing the client’s request to the appropriate back-end server.
-
A firewall for web applications. This protects your application by filtering incoming and outgoing network requests on your server.
The hard limits#
To address security issues, the server respects the soft limits fixed in the configuration file. In addition the server is configured with hard limits, defined at compile time:
The sweet spot is 16K per header and 128K total. That is large enough for SSO tokens, but not so large that you waste memory.
In the event of a hard limit being exceeded, the server terminates the connection without returning a status (431). This behavior helps to evade certain malicious queries, by considering that the message does not conform to the protocol and therefore a formatted response is not necessary.
Starting the server#
The server process could be started as daemon or attached to the console,
i.e to validate the configuration file using option --test.
wbs_server --test --config=/etc/wbs/server.confThe most common usage is to daemonize the process using --daemon argument.
wbs_server --daemon --config=/etc/wbs/server.confStopping the server#
The server process will stop on signal INT.
kill -INT <pid>Reloading the configuration#
The server process will reload the configuration on signal HUP.
kill -HUP <pid>During the reload, request processing will not be interrupted. The previous configuration will remain available until the associated client requests have finished processing. However, new requests will be processed using the new configuration.
Basic authentication#
Basic authentication refers to a password file. A password file can contain as many permissions as needed, and each permission is defined on one line with the following format.
<USER>:<PASSWORD>
Using the command wbs_encrypt you can encrypt passwords added in plain
text, as shown below.
wbs_encrypt <password_file>Configuration File’s Structure#
WBS consists of modules which are controlled by directives specified in the configuration file. Directives are divided into simple directives and block directives. A simple directive consists of the name and parameters separated by spaces and ends with a semicolon (;). A block directive has the same structure as a simple directive, but instead of the semicolon it ends with a set of additional instructions surrounded by braces ({ and }). If a block directive can have other directives inside braces, it is called a context (examples: server, location, and exec).
Directives placed in the configuration file outside of any contexts are considered to be in the main context. The servers directives reside in the main context, location in server, and exec in location.
A block of text surrounded by (/* and */) is considered a comment. A line begining with # sign is considered a commentted line.
For example:
daemon_log /tmp/error.log;
load_module /opt/wbs/modules/libwbs_www.so;
server {
port 8080;
access_log /tmp/access_8080.log;
location / {
document_root /var/www/html;
exec www {
mime_types /opt/wbs/mimetypes.conf;
index index.html;
}
}
}General syntax of directives#
document = body
body = *( directive | block )
directive = token [ value | token ] ";"
block = context [ value ] "{" body "}"
context = "server" | "location" | "exec"
value = <"> TEXT <"> | TEXT excluding special | BOOL | TIMEOUT
special = LWS | "{" | "}" | ";" | <">
LWS = SP | \t | \v | \f | \r | \n
BOOL = "true" | "false"
TIMEOUT = float+timeunit
timeunit = "ms" | "s" | "m" | "h" | "d"
SIZE = float+sizeunit
sizeunit = "" | "k" | "m" | "g"Bindings variables#
Some directives can accept a value containing bound variables. A set of predefined variables allow values to be adapted to the context of the client request. The complete list follows.
Dynamic variables derived from regular expressions can also be used. In
this case, they are accessed by their extraction rank: $0 for the
complete matching string, then $1… $n for substrings.
Other directives accept one or more specific variables; in this case, refer to their documentation.
For example the following text will produce an URL of the request.
“$scheme://$host$request”
“$scheme://$host$uri$is_query$query”
The predefined variable#
- “$host”
The value of the header HOST.
i.e: www.domain.org , www.domain.org:8443.
- “$server_name”
The substring HOSTNAME or ADDRESS of the header HOST, therefore without the PORT number.
- “$server_port”
The substring PORT of the header HOST. It could be empty.
- “$server_addr”
The IP address of the server from which the connection is established.
- “$remote_addr”
The IP address of the remote client.
- “$scheme”
The scheme of the request: http or https.
- “$method”
The method of the request, i.e GET , POST or other.
- “$request”
The original request. It includes the absolute path and query, with original encoding.
- “$uri”
The absolute path with encoding.
- “$is_query”
The chararcter ? if the request has a query part, else an empty string.
- “$query”
The query with with original encoding.
Exploding the request#
GET /my%20app/Hello%2FWorld%3F/?id=abcd&tag=%23some%20data HTTP/1.1
Host: www.domain.org:8443$method = GET
$request = /my%20app/Hello%2FWorld%3F/?id=abcd&tag=%23some%20data
$uri = /my%20app/Hello%2FWorld%3F/
$is_query = ?
$query = id=abcd&tag=%23some%20data
$host = www.domain.org:8443
$server_name = www.domain.org
$server_port = 8443
Finally, the path to the location is normalized by decoding the URI. Be aware that encoded reserved characters will not be normalized:
ALPHA DIGIT - . _ ~ /! $ & ' ( ) * + , ; =% @ : [ | ]
For example, the pattern %2F or %2E remains unchanged during
normalization. In contrast, the pattern %3F will be decoded.
When applicable, the variable $pathinfo is defined as follows.
For location / => $pathinfo is "[/]my app/Hello%2FWorld?/"
For location "/my app" => $pathinfo is /Hello%2FWorld?/
For location "/my app/" => $pathinfo is /Hello%2FWorld?/
For location "/my app/Hello%2FWorld?" => $pathinfo is /
For location "/my app/Hello%2FWorld?/" => $pathinfo is [/]
The main context#
directive = "daemon_log" ; set error log file
| "debug_level" ; set the logging level
| "load_module" ; include a module
| "server" ; start a new context serverDirective “daemon_log”#
Configure the path to the error log file.
"daemon_log" value ;
Directive “debug_level”#
Configure the logging level.
"debug_level" value ;
value = 0 (ERR) | 1 (WRN) | 2 (INF) | 3 (DBG) | 4 (PRO) | 6 (ALL)
Directive “load_module”#
Load a module from specified path.
"load_module" value ;
The block server#
A context server declares the setup for a new server. It should be start from the root, and after the declaration of any required modules.
"server" "{" body "}"
The body allows directives as follows.
directive = "port" ; set listen PORT
| "ipv6" ; enable IPv6
| "listener_queue_size" ; accept queue size
| "thread_pool_size" ; the maximum number of threads
| "thread_keep_alive" ; keep inactive thread alive for time
| "request_queue_size" ; limit of pending request
| "access_log" ; set access log file
| "client_timeout" ; set client connection timeout
| "ssl" ; enable SSL
| "ssl_certificate_file" ; SSL certificate file
| "ssl_private_key_file" ; SSL private key file
| "location" ; start a new context locationDirective “port”#
Configure the TCP port to bind: [ 1 , 65000 ]
"port" value ;
Directive “ipv6”#
Enable IPv6: ( true | false ), default false, no value is true.
"ipv6" [ BOOL ] ;
Directive “listener_queue_size”#
Configure the accept queue size: [ 1 , 4096 ], default 127.
"listener_queue_size" value ;
Directive “thread_pool_size”#
Configure the number of worker threads: [ 4 , 1024 ], default 10.
"thread_pool_size" value ;
See also the directive “request_queue_size”.
Directive “thread_keep_alive”#
Configure the timeout to close an inactive thread: [ 0ms , 48d ], default 60s.
"thread_pool_size" TIMEOUT ;
i.e: 30000ms | 30s | 0.5m | 6h | 3d.
Directive “request_queue_size”#
Configure the limit of pending request: [ 20 , 4096 ], default 256. The server will not accept any new client connections if the limit is reached, and it will wait for the queue to decrease before accepting new connections.
"request_queue_size" value ;
The value should be 5 times or more than “thread_pool_size”, dependings on the hardware performance. See also the directive “listener_queue_size”.
Directive “access_log”#
Configure the path to the access log file.
"access_log" value ;
Directive “client_timeout”#
Configure the timeout for all accepted client connection: [ 1s , 60s ], default 5s.
"client_timeout" TIMEOUT ;
i.e: 5s | 0.5m | 1m.
Note that the value is rounded to the nearest second.
Directive “ssl”#
Enable SSL: ( true | false ), default false, no value is true.
"ssl" [ boolean ] ;
boolean = "true" | "false"
Directive “ssl_certificate_file”#
Set the path to the SSL certificate file (pem,crt).
"ssl_certificate_file" value ;
Directive “ssl_private_key_file”#
Set the path to the SSL private key file (pem,key).
"ssl_private_key_file" value ;
The block location#
A context location declares a handler for an URI path. This path is virtual and, if necessary, can be associated with a physical path (document root). The block is declared in a context server to which it refers. The specified path is assumed to be normalized and therefore not encoded.
"location" path "{" body "}"
path = TEXT | <"> TEXT <">
The body allows directives as follows.
directive = "document_root" ; physical root path of resources
| "auth_basic" ; enable basic authentication
| "auth_user_file" ; password file containing challenges
| "log_access" ; enable/disable the logging
| "try" ; list of resources to check in order
| "return" ; break and return status
| "exec" ; start a new context execDirective “document_root”#
Configure the document root for this location. The location path should exist in the specified root. The directive is required by some modules that use static resources (www, fastcgi, or cgi).
"document_root" path ;
Directive “auth_basic”#
Enable the basic authentication for this location.
"auth_basic" [ BOOL ] ;
Directive “auth_user_file”#
Set the path to the password file containing challenges for authentication.
"auth_user_file" path ;
Directive “log_access”#
Enable or disable the access logging for this location. By default access logging is enabled.
"log_access" [ BOOL ] ;
Directive “try”#
Configure the rewrite rules of the PATHINFO for this location. The rewritten paths will be tried in order.
"try" *( path ) ;
Use the bind variable “$pathinfo” to substitute the original PATHINFO of the requested resource. The PATHINFO is relative to the path of the location, and like the location it is normalized. The following examples will explain this even better.
try $pathinfo /index.php$pathinfo
try $pathinfo /404_custom.html
Directive “return”#
Stops processing the request if no handler has processed the resource (i.e., not found or not applicable) at that location. The server returns a response with the specified status code. Without this directive, the server tries the next location block.
"return" status ;
status = ( 4xx | 5xx)
The block exec#
A context exec declares the setup of module instance that will process the request in the context of parent location. All module instances in a location will be executed in the order they were declared, until the request is resolved. Finally if no response could be provided, the appropriate status code is returned.
An optional filter on the PATHINFO can be defined to activate the module
instance based on specific criteria. Note that PATHINFO is always prefixed
with a separator / unless it is null.
"exec" module [ "for" regex_filter ] "{" body "}"
module = TEXT ; name of any pre-loaded module in the main context
regex_filter = TEXT | <"> TEXT <">
For example:
location /myapp {
document_root /var/www;
try $pathinfo /index.php$pathinfo;
/* only php */
exec fastcgi for ".*\\.php(?:$|/.*)" {
split_path_info "^(.+?\\.php)(\\/.*|)$";
endpoint tcp://127.0.0.1:9000;
post_max_size 128k;
chunk_size 64k;
}
/* no php */
exec www for "^((?!\\.php).)*$" {
mime_types /opt/wbs/mimetypes.conf;
add_header Cache-Control "max-age=86400";
chunk_size 64k;
}
}Body of context exec depends of the selected module.
The standard modules#
The module www#
directive = "mime_types" ; path to the file of types
| "allow_browse" ; allow/deny directory index
| "allow_hidden" ; allow/deny hidden paths
| "index" ; default index files
| "chunk_size" ; set the transfer chunk size
| "add_header" ; set extra headerDirective “mime_types”#
Set the path to the configuration file of types.
"mime_types" path ;
Directive “allow_browse”#
Allow or deny directory index.
"allow_browse" [ BOOL ] ;
Directive “allow_hidden”#
Allow or deny hidden paths. Any path through a directory or file prefixed with a dot is considered hidden.
"allow_hidden" [ BOOL ] ;
Directive “index”#
Configure the list of index files to be tried when the requested resource is a directory. The files will be tried in order.
"index" *( file ) ;
Directive “chunk_size”#
Set the size of chunk for chunked transfers: [ 1k , 64k ], default 16k.
"chunk_size" SIZE ;
Directive “add_header”#
Add a response header. The value can use substitution variables.
"add_header" name value ;
name = TEXT
value = TEXT | <"> TEXT <">
For example:
add_header Cache-Control "max-age=86400";
The module cgi#
The module implements the Common Gateway Interface (CGI) Version 1.1. See (RFC 3875).
directive = "interpreter" ; path to the interpreter
| "argument" ; list of call argument
| "environ" ; list of environ
| "index" ; default index files
| "chunk_size" ; set the transfer chunk size
| "post_max_size" ; limit client body size
| "process_timeout" ; limit execution time
| "split_path_info" ; extract script_name and path_info
| "add_header" ; set extra headerDirective “interpreter”#
Set the path to the program that is called.
"interpreter" path ;
The program is called with the arguments followed by the script path.
Directive “argument”#
Add a list of arguments for the program call.
"argument" [ string ] ;
Directive “environ”#
Add a list of environ for the program call.
"environ" [ string ] ;
For example:
environ "REDIRECT_STATUS=200";
Directive “index”#
Configure the list of index files to be tried when the requested resource is a directory. The files will be tried in order.
"index" *( file ) ;
Directive “chunk_size”#
Set the size of chunk for chunked transfers: [ 1k , 64k ], default 16k.
"chunk_size" SIZE ;
Directive “post_max_size”#
Set the maximum length of request body: [ 0, max_long ], default 64k.
"post_max_size" SIZE ;
The body of the request is passed to the interpreter via the stream STDIN, as specified by the protocol.
Directive “process_timeout”#
Defines a timeout for the transmission of the whole response. If the child process does not complete within this time, it is aborted and the connection is closed.
Range of value: [ 1s , 1d ], default 60s.
"process_timeout" TIMEOUT ;
i.e: 30000ms | 30s | 0.5m | 6h | 1d.
Directive “split_path_info”#
Define the regular expression to extract values for the environ SCRIPT_NAME and PATH_INFO.
"split_path_info" regex_sub ;
regex_sub = TEXT | <"> TEXT <">
The expression must provide two substrings, respectively assigned to SCRIPT_NAME and PATH_INFO.
For example:
split_path_info "^(.+?\\.php)(\\/.*|)$" ;Directive “add_header”#
Add a response header. The value can use substitution variables.
"add_header" name value ;
name = TEXT
value = TEXT | <"> TEXT <">
The module fastcgi#
The module allows passing requests to a FastCGI server.
directive = "endpoint" ; connection description to the server
| "environ" ; list of environ
| "index" ; default index files
| "chunk_size" ; set the transfer chunk size
| "post_max_size" ; limit client body size
| "read_timeout" ; server read timeout
| "split_path_info" ; extract script_name and path_info
| "add_header" ; set extra header
| "trace_request" ; enable/disable protocol debuggingDirective “endpoint”#
Set the connection descriptor to the FastCGI server.
"endpoint" socket_desc;
socket_desc = proto ":" ip_addr ":" port
proto = "tcp"
For example:
endpoint tcp:127.0.0.1:9000 ;
Directive “environ”#
Add a list of environment variables to pass parameters to the application.
"environ" [ string ] ;
For example:
environ "REDIRECT_STATUS=200";
Directive “index”#
Configure the list of index files to be tried when the requested resource is a directory. The files will be tried in order.
"index" *( file ) ;
Directive “chunk_size”#
Set the size of chunk for chunked transfers: [ 1k , 64k ], default 16k.
"chunk_size" SIZE ;
Directive “post_max_size”#
Set the maximum length of request body: [ 0, max_long ], default 64k.
"post_max_size" SIZE ;
The body of the request is passed to the interpreter via the stream STDIN, as specified by the protocol.
Directive “read_timeout”#
Defines a timeout for reading a response from the FastCGI server. The timeout is set only between two successive read operations, not for the transmission of the whole response. If the FastCGI server does not transmit anything within this time, the connection is closed.
Range of value: [ 1s , 1d ], default 60s.
"read_timeout" TIMEOUT ;
i.e: 30000ms | 30s | 0.5m | 6h | 1d.
Directive “split_path_info”#
Define the regular expression to extract values for the environ SCRIPT_NAME and PATH_INFO.
"split_path_info" regex_sub ;
regex_sub = TEXT | <"> TEXT <">
The expression must provide two substrings, respectively assigned to SCRIPT_NAME and PATH_INFO.
For example:
split_path_info "^(.+?\\.php)(\\/.*|)$" ;Directive “add_header”#
Add a response header. The value can use substitution variables.
"add_header" name value ;
name = TEXT
value = TEXT | <"> TEXT <">
The module filter#
The module allows filtering of request on the PATHINFO.
directive = "allow" ; allow request
| "deny" ; deny requestDirective “allow”#
Request with machted PATHINFO is allowed to proceed to the next step.
"allow" regex_filter;
regex_filter = TEXT | <"> TEXT <">
Directive “deny”#
Request with matched PATHINFO is not allowed to continue. The specified status or “403 Forbidden” is returned, and the connection is closed.
"deny" regex_filter [ status ] ;
status = ( 4xx | 5xx)
The module proxy#
The module allows passing requests to another server.
directive = "proxy_pass" ; url of the proxied server
| "proxy_set_header" ; add/replace request header
| "proxy_add_header" ; add request header
| "proxy_redirect" ; redirect rule
| "proxy_preserve_host" ; preserve request host
| "proxy_upgrade_sessions" ; max upgraded sessions at time
| "read_timeout" ; server read timeout
| "buffer_size" ; size of the buffer
| "add_header" ; set extra header
| "trace_request" ; enable/disable protocol debuggingDirective “proxy_pass”#
Set the URL to the proxied server.
"proxy_pass" url;
url = TEXT | <"> TEST <">
For example:
proxy_pass http://192.168.1.1/myapp ;
Directive “proxy_set_header”#
Allows redefining fields to the request header passed to the proxied server. The value can use substitution variables.
"proxy_set_header" name value ;
name = TEXT
value = TEXT | <"> TEXT <">
Directive “proxy_add_header”#
Allows appending fields to the request header passed to the proxied server. The value can use substitution variables. If the request header exists then the value will be appended, else the header is added with the given value.
"proxy_add_header" name value ;
name = TEXT
value = TEXT | <"> TEXT <">
Directive “proxy_redirect”#
Sets the text that should be changed in the “Location” and “Content- Location” header fields of a proxied server response. The strings “redirect” and “replacement” can use substitution variables.
"proxy_redirect" ( redirect replacement | BOOLEAN ) ;
When no rule is defined, you can disable the default redirect rules using the directive followed by a boolean.
For example:
proxy_redirect http://proxied:8000/two/ http://frontend/one/ ;
proxy_redirect /two $scheme://$host/one ;
proxy_redirect false ;
Directive “proxy_preserve_host”#
Uses the Host request header for the proxy request.
"proxy_preserve_host" BOOLEAN ;
Directive “proxy_upgrade_sessions”#
Defines the maximum count of upgraded sessions at time. Upgrading session to raw stream (i.e websocket) keeps the thread alive until session end. Therefore, the value must be significantly lower than the thread pool size of the server.
Range of value: [ 0 , long ], default 0.
"proxy_upgrade_sessions" integer ;
Note: To allow websocket streams, set the value greater than 0, and adjust the thread pool size of the server.
Directive “read_timeout”#
Defines a timeout for reading a response from the proxied server. The timeout is set only between two successive read operations, not for the transmission of the whole response. If the proxied server does not transmit anything within this time, the connection is closed.
Range of value: [ 1s , 1d ], default 60s.
"read_timeout" TIMEOUT ;
i.e: 30000ms | 30s | 0.5m | 6h | 1d.
Directive “buffer_size”#
Set the size of data buffer: [ 16k , 64k ], default 16k.
"buffer_size" SIZE ;
Directive “add_header”#
Add a response header. The value can use substitution variables.
"add_header" name value ;
name = TEXT
value = TEXT | <"> TEXT <">
The module rewrite#
The module is used to change request URI using regular expressions, return redirects, and conditionally select configurations.
directive = "if" ; conditional select
| "redirect" ; unconditional redirect
| "rewrite" ; unconditional rewrite
| "return" ; unconditional returnFor example:
if $server_name == www.foo.org redirect $scheme://foo.org/$uri;
if $server_name != foo.org redirect $scheme://foo.org/$uri;
if $uri matches "/apps/(.*|$)" rewrite /cgi/bonjour/$1 break;
rewrite /captcha$uri;
Directive “if”#
The specified condition is evaluated. If true, this module directives specified as follows are executed. The values can use substitution variables.
"if" TEXT ( "matches" regex_sub | ( "==" | "!=" ) TEXT ) directive ;
regex_sub = TEXT | <"> TEXT <">
directive = rewrite | redirect | return
Directive “redirect”#
Request is redirected to the specified location. Adding the flag “permanent” redirects with status “301 Moved Permanently”, else “302 Moved Temporaly”. The location can use substitution variables.
"redirect" url [ "permanent" ] ;
url = TEXT | <"> TEXT <">
The new location could be absolute (with scheme) or relative.
Directive “rewrite”#
The URI is rewritten as specified. Adding the flag “break” exit the rules proceesing loop, and the server retry the processing with the rewritten URI.
"rewrite" uri [ "break" ] ;
uri = TEXT | <"> TEXT <">
The new uri should be encoded and absolute (starting with /).
If conditional matching is used then matched sub-strings could be used as bind variables in the rewritten string. Bind variable use the following naming.
$0: the entire string$n: the corresponding substring of rank n
Directive “return”#
Stops processing the request, and returns a response with the specified status code.
"return" status ;
status = ( 4xx | 5xx)