YAWS.CONF(5)

πŸ“¦
version 2.3.1 Β· 2 versions
, in nixpkgs unstable, ubuntu stonking

🌸 so you wanna read the yaws.conf(5) manual? periodt ✨

hiii bestie πŸ’– okay so this is the User Commands manual page for yaws.conf(5), and honestly? it's a whole journey. grab a snack, we're doing this together 🫢


🌈 NAME

/etc/yaws/yaws.conf β€” the configuration file for the Yaws web server. that's it, that's the name, iconic πŸ’…


πŸ¦‹ DESCRIPTION

So Yaws? she's a fast lightweight web server πŸ‘‘ and she reads a configuration file called yaws.conf to, like, control everything she does. and this config file has two distinct parts, okay, listen:

  • a global part β€” affects all the virtual hosts. all of them. no cap.
  • a server part β€” where the options for each virtual host get supplied.

two parts, two vibes, one file. slay ✨


πŸ’œ GLOBAL PART

this is where the big-picture settings live, bestie. the main character energy settings. let's go one by one 🌈

  • logdir = [+]Directory β€” all Yaws logs get written to files in this directory πŸ“ if you specify it with a +, Yaws will attempt to create the directory if it does not exist. and bestie, Yaws writes several different log files, so let's meet them:
    • report.log β€” a text file containing all error logger printouts from Yaws. the receipts file 😭
    • <Host>.access β€” for each virtual host served by Yaws, a file <Host>.access gets written containing an access log in NCSA combined/XLF/ELF log format. (peep http://www.w3.org/TR/WD-logfile.html for more details on Extended Log File Format, iykyk)
    • <Host>.auth β€” for each virtual host served by Yaws, a file <Host>.auth gets written containing all http auth related messages πŸ”
    • trace_<YYYYMMDD_hhmmss> β€” trace files get written in this subdirectory, suffixed by the creation date. and there's two flavors:
      • trace.<Pid>.http β€” contains the HTTP trace if that's enabled, where <Pid> is the process id handling the TCP connection.
      • trace.<Pid>.traffic β€” contains the traffic trace if that's enabled, where <Pid> is the process id handling the TCP connection.
heads up babe: the <Host>.access and <Host>.auth files get used only if the directive logger_mod is not set, or is set to yaws_log. and the default value for logdir is "." πŸŽ€
  • ebin_dir = Directory β€” adds Directory to the Erlang search path. you can have several of these commands in the config file, go off πŸ’« the default value is "yaws_dir"/examples/ebin.
  • src_dir = Directory β€” defines a Directory as a source directory. Yaws will compile all erlang modules found in this directory and all its subdirectories πŸ”₯ the compilation happens when the configuration is loaded or reloaded. the include_dir directives are used to search for include files. multiple src_dir directives are allowed. and there is no such directory configured by default β€” nope, zero, none πŸ™ˆ
  • id = String β€” bestie you can run multiple Yaws servers on the same machine 😱 we use the id of a Yaws server to control it using the different control commands, like:
# /usr/local/bin/yaws --id foobar --stop

which stops the Yaws server with id "foobar". each Yaws server writes its internal data into a file called $HOME/.yaws/yaws/ID where ID is the identity of the server. and Yaws also creates a file called $HOME/.yaws/yaws/ID/CTL which contains the port number where the server is listening for control commands πŸ‘€ the default id is "default".

  • server_signature = String β€” sets the "Server: " output header to the custom value πŸ’… the default value is "yaws/%VSN%, Yet Another Web Server".
  • include_dir = Directory β€” adds Directory to the path of directories where the Erlang compiler searches for include files. we need this if we want to include .hrl files in our Yaws Erlang code 🧚 several of these commands are allowed in the config file. the default value is "yaws_dir"/examples/include.
  • max_num_cached_files = Integer β€” Yaws caches small files like commonly accessed GIF images in RAM, and this directive sets a maximum number on the number of cached files πŸ“ the default value is 400.
  • max_num_cached_bytes = Integer β€” controls the total amount of RAM which can maximally be used for cached RAM files. the default value is 1000000, aka 1 megabyte ✨
  • max_size_cached_file = Integer β€” sets a maximum size on the files that are RAM cached by Yaws. the default value is 8000, aka 8 kBytes.
  • cache_refresh_secs = Integer β€” the RAM cache is used to serve pages that sit in the cache, and an entry sits in cache at most cache_refresh_secs number of seconds ⏰ the default is 30. which means when the content is updated under the docroot, that change doesn't show until 30 seconds have passed 😭 ngl while developing a Yaws site it may be convenient to set this value to 0. and if the debug flag (-d) is passed to the Yaws start script, this value is automatically set to 0. no cap.
  • trace = false | traffic | http β€” enables traffic or http tracing πŸ” tracing is also possible to enable with a command line flag to Yaws. default is false.
  • auth_log = true | false β€” deprecated and ignored πŸ’€ now, this target must be set in the server part. not here, bestie, not here.
  • max_connections = nolimit | Integer β€” set this value to control the maximum number of connections from HTTP clients into the server. this is implemented by closing the last socket if the limit threshold is reached πŸšͺ
  • keepalive_maxuses = nolimit | Integer β€” normally Yaws does not restrict the number of times a connection is kept alive using keepalive. setting this parameter to an integer X will ensure that connections are closed once they've been used X times 🫢 this can be useful to guard against long running connections collecting too much garbage in the Erlang VM. (yes, garbage, she's a messy queen but she handles it)
  • process_options = undefined | Proplist β€” sets process spawn options for client acceptor processes. options must be specified as a quoted string of either the atom undefined or as a proplist of valid process options. the supported options are fullsweep_after, min_heap_size, and min_bin_vheap_size, each taking an associated integer value πŸ”’ other process options are ignored. the proplist may also be empty. see erlang:spawn_opt/4 for details on these options.
  • large_file_chunk_size = Integer β€” sets the chunk size used by Yaws to send large files πŸ“¦ the default value is 10240.
  • large_file_sendfile = erlang | disable β€” sets the version of sendfile method to use to send large files:
    • erlang β€” use file:sendfile/5.
    • disable β€” use gen_tcp:send/2.

the default value is erlang. πŸ’«

  • acceptor_pool_size = Integer β€” sets the size of the pool of cached acceptor processes. the specified value must be greater than or equal to 0 ⚠️ the default value is 8. and specifying a value of 0 effectively disables the process pool. okay werk, zero.
  • log_wrap_size = Integer β€” the logs written by Yaws are all wrap logs 🌯 the default value at the size where they wrap around, and the original gets renamed to File.old, is 1000000, aka 1 megabyte. this value can be changed. if we set the value to 0 the logs will never wrap. and if we want to use Yaws in combination with a more traditional log wrapper such as logrotate, set the size to 0 and Yaws will reopen the logfiles once they've been renamed/removed πŸ‘‘
  • log_resolve_hostname = true | false β€” by default the client host IP is not resolved in the access logs. that's the default, bestie πŸ™ˆ
  • fail_on_bind_err = true | false β€” fail completely or not if Yaws fails to bind a listen socket. default is true. she either goes all in or she doesn't, main character behavior πŸ’…
  • enable_soap = true | false β€” if true, a soap server will be started at startup of Yaws 🧼 default is false.
  • soap_srv_mods = ListOfModuleSetting β€” if enable_soap is true, a startup Yaws will invoke yaws_soap_srv:setup() to setup the modules set here. ModuleSetting is either a triad like <Mod, HandlerFunc, WsdlFile> or a quadruple form like <Mod, HandlerFunc, WsdlFile, Prefix> which specifies the prefix ✨ a prefix will be used as argument of yaws_soap_lib:initModel() and then be used as a XML namespace prefix. note, the WsdlFile here should be an absolute-path file in local file systems 🚨

for example, we can specify

soap_srv_mods=<Mod1, Handler, Wsdl1> <Mod2, Handler, Wsdl2, Prefix> ...
  • php_exe_path = Path β€” this target is deprecated and useless πŸ’€ use 'php_handler' target in server part instead. the name of (and possibly path to) the php executable used to interpret php scripts (if allowed). default is php_exe_path = php-cgi.
  • copy_error_log = true | false β€” enable or disable copying of the error log πŸ“‹ when we run in embedded mode, there may very well be some other systems process that is responsible for writing the errorlog to a file, whereas when we run in normal standalone mode, we typically want the Erlang errorlog written to a report.log file. default value is true.
  • ysession_mod = Module β€” allows specifying a different Yaws session storage mechanism instead of an ETS table πŸ—„οΈ one of the drawbacks of the default yaws_session_server implementation is that server side cookies are lost when the server restarts 😭 specifying a different module here will pass all write/read operations to this module (it must implement appropriate callbacks).
  • ysession_cookiegen = Module β€” allows specifying a different Yaws session cookie generator than the built-in default πŸͺ Module is expected to provide a new_cookie/0 function that returns a session cookie in the form of a list. such a cookie generator module must be careful to return a unique cookie each time it's called ⚠️ no dupes allowed.
  • ysession_idle_timeout = Integer β€” controls Yaws session idle cleanup. if a server has been idle for ysession_idle_timeout milliseconds, check all Yaws sessions and remove any that have timed out ⏳ the default ysession_idle_timeout value is 2*60*1000 (2 minutes).
  • ysession_long_timeout = Integer β€” controls Yaws session periodic cleanup. every ysession_long_timeout milliseconds, check all Yaws sessions and remove any that have timed out 🧹 the default ysession_long_timeout value is 60*60*1000 (1 hour).
  • runmod = ModuleName β€” at startup Yaws will invoke ModuleName:start() in a separate process πŸš€ it's possible to have several runmods. this is useful if we want to reuse the Yaws startup shell script for our own application. cute, right? reuse, recycle, slay ♻️
  • pick_first_virthost_on_nomatch = true | false β€” okay so when a request rolls up, Yaws grabs the Host header and uses it to pick which virtual server (out of everyone sharing that same IP/Port combo) should get the vibe ✨. This knob decides whether Yaws just picks the first server it finds in the yaws.conf file when there's no name match. If this is false and the request has no Host header at all? Yaws sends back a 400 Bad Request, exactly like the HTTP standard demands, no arguing πŸ’…. In real live hosting you lowkey want this false, but in testing/development it's so convenient to just set it true. Default is true.
  • keepalive_timeout = TimeInMilliseconds | infinity β€” if the HTTP session is being kept alive (i.e., not immediately closed), it'll shut itself down after keepalive_timeout milliseconds, unless a new request shows up in that window. Default value is 30000. The value infinity is legal but ngl, not recommended πŸ™ˆ.
  • subconfig = File β€” loads a specified config file. Absolute paths, or relative ones pointing at the configuration location, are both allowed. Unix-style wildcard strings can grab several files at once β€” peep filelib:wildcard/1 for the deets. Hidden files, the ones starting with a dot, get ignored πŸ«₯. For example:


  subconfig = /etc/yaws/global.conf
  subconfig = /etc/yaws/vhosts/*.conf

Or, relatively to the configuration location:



  subconfig = global.conf
  subconfig = vhosts/*.conf
  • subconfigdir = Directory β€” loads all the config files it finds in the specified directory, bestie. The given Directory can be an absolute path or relative to the configuration location. Hidden files, starting by a dot, will be ignored.
  • x_forwarded_for_log_proxy_whitelist = ListOfUpstreamProxyServerIps β€” this target is deprecated and will be ignored πŸ’€.
  • default_type = MimeType β€” defines the default MIME type used wherever Yaws can't figure it out from its MIME types mappings. Default is text/plain.
  • default_charset = Charset β€” defines the default charset that gets added when a response content-type is text/*. By default, no charset is added.
  • mime_types_file = File β€” overrides the default mime.types file that ships with Yaws. This file has to use the following format:
  # Lines beginning with a '#' or a whitespace are ignored
  # blank lines are also ignored
  <MIME type> <space separated file extensions>

The default file lives at ${PREFIX}/lib/yaws/priv/mime.types. You should not edit this file because it may get replaced when you upgrade your server πŸ™.

  • add_types = ListOfTypes β€” specifies one or more mappings between MIME types and file extensions. More than one extension can be assigned to a MIME type. ListOfTypes is defined like this:
  add_types = <MimeType1, Ext> <MimeType2, Ext1 Ext2 ...> ...

The mappings defined using this directive will overload all other definitions. If a file extension is defined several times, only the last one is kept. Multiple add_types directives may be used. Slay πŸŽ€

  • add_charsets = ListOfCharsets β€” specifies one or more mappings between charsets and file extensions. More than one extension can be assigned to a charset. ListOfCharsets is defined like this:
  add_charsets = <Charset1, Ext> <Charset2, Ext1 Ext2 ...> ...

The mappings defined using this directive will overload all other definitions. If a file extension is defined several times, only the last one is kept. Multiple add_charsets directives may be used.

  • sni = disable | enable | strict β€” turns TLS SNI (Server Name Indication) support on or off ✨.

When disabled (or not supported), all virtual servers in the same group (same IP/Port) must share the same SSL configuration, especially the same SSL certificate. Only the HTTP Host header will be considered when finding the right virtual server.

When enabled, SSL config can be different from one virtual server to another β€” each one can have its own SSL certificate πŸ’…. In that case, if a client provides a SNI hostname, it gets used to find the right virtual server. To accept the SNI information from the client, the first virtual server β€” the default one, see pick_first_virthost_on_nomatch β€” must include TLS as a permitted protocol.

If the sni directive is set to enable, non-SNI clients are allowed. For those clients, virtual servers are selected as if Yaws didn't have SNI support at all. If it's set to strict, the SNI hostname is mandatory to access an SSL virtual server 🚨. But in all cases, when SNI support is enabled, if a client provides a SNI hostname, it must match the HTTP Host header (which is mandatory too). Note that the first virtual server (the default one) will be used for any request where the provided SNI hostname doesn't match any of the virtual server names. So it is important that the first virtual server has the most restrictive access control β€” otherwise clients can access restricted resources by sending a request for any unknown hostname πŸ™ˆ. (This isn't actually any different from using virtual servers without SNI support.) If you're using self-signed certificates, be sure to also set the depth configuration variable to 0 to avoid following certificate chains.

The sni directive is a global one, so if you set it to strict, non-SNI clients will be refused for all SSL groups. See the require_sni directive from the server part to mitigate this requirement.

Default is disable.


πŸ‘‘ SERVER PART πŸ‘‘

Yaws can virthost several web servers on the same IP address as well as several web servers on different IP addresses. This includes SSL servers πŸ”₯.

Each virtual host is defined within a matching pair of <server ServerName> and </server>. The ServerName will be the name of the webserver.

The following directives are allowed inside a server definition.

  • port = Port β€” this makes the server listen on Port. Default is 8000.
  • listen = IpAddress β€” this makes the server listen on IpAddress. When virthosting several servers on the same ip/port address, if the browser doesn't send a Host: field, Yaws will pick the first server specified in the config file. If the specified IP address is 0.0.0.0, Yaws will listen on all local IP addresses on the specified port. Default is 127.0.0.1. Multiple listen directives may be used to specify several addresses to listen on.
  • listen_backlog = Integer β€” this sets the TCP listen backlog for the server, defining the maximum length the queue of pending connections is allowed to grow to. The default is 1024.
  • <listen_opts> ... </listen_opts> β€” defines extra options to be set on the listen socket and, by inheritance, on accepted sockets. Peep inet:setopts/2 for details. Supported options are:
    • buffer = Integer (default: same as inet:setopts/2)
    • delay_send = true | false (default: same as inet:setopts/2)
    • linger = Integer | false (default: same as inet:setopts/2)
    • nodelay = true | false (default: same as inet:setopts/2)
    • priority = Integer (default: same as inet:setopts/2)
    • sndbuf = Integer (default: same as inet:setopts/2)
    • recbuf = Integer (default: same as inet:setopts/2)
    • send_timeout = Integer | infinity (default: same as inet:setopts/2)
    • send_timeout_close = true | false (default: same as inet:setopts/2)
  • server_signature = String β€” this directive sets the "Server: " output header to the custom value and overloads the global one for this virtual server πŸ’….
  • subconfig = File β€” same as the subconfig directive of the global part, but here the files should only contain directives allowed in the server part.
  • subconfigdir = Directory β€” same as the subconfigdir directive of the global part, but here the files should only contain directives allowed in the server part.
  • rhost = Host[:Port] β€” this forces all local redirects issued by the server to go to Host. Super useful when Yaws listens to a port that's different from the port the user actually connects to. For example, running Yaws as a non-privileged user makes it impossible to listen to port 80, since that port can only be opened by a privileged user. Instead Yaws listens to a high port number, 8000, and iptables are used to redirect traffic to port 80 over to port 8000 (most NAT:ing firewalls will also do this for you 🫢).
  • rmethod = http | https β€” this forces all local redirects issued by the server to use this method. Handy when an SSL off-loader, or stunnel, is sitting in front of Yaws.
  • auth_log = true | false β€” enable or disable the auth log for this virtual server. Default is true.
  • access_log = true | false β€” setting this directive to false turns off traffic logging for this virtual server. The default value is true.
  • logger_mod = Module β€” it's possible to set a special module that handles access and auth logging ✨. The default is to log all web server traffic to <Host>.access and <Host>.auth files in the configured or default logdir. This module must implement the behaviour yaws_logger. Default value is yaws_log.

The following functions should be exported:

  • Module:open_log(ServerName, Type, LogDir) β€” when Yaws is started, this function is called for this virtual server. If the initialization is successful, the function must return {true,State} and if an error occurred, it must return false.
  • Module:close_log(ServerName, Type, State) β€” this function is called for this virtual server when Yaws is stopped.
  • Module:wrap_log(ServerName, Type, State, LogWrapSize) β€” this function is used to rotate log files. It is regularly called by Yaws and must return the possibly updated internal NewState.
  • Module:write_log(ServerName, Type, State, Infos) β€” when it needs to log a message, Yaws will call this function. The parameter Infos is {Ip,Req,InHdrs,OutHdrs,Time} for an access log and {Ip,Path,Item} for an auth log, where:
  • Ip β€” the IP address of the client that came knocking, as a tuple πŸ’«
  • Req β€” the HTTP method, the URI path and the HTTP version of the request, all bundled up as a #http_request{} record
  • InHdrs β€” the HTTP headers that arrived from the WWW client, as a #headers{} record πŸ“¨
  • OutHdrs β€” the HTTP headers that got sent back to the WWW client, as a #outh{} record
  • Path β€” the URI path of the request, as a string
  • Item β€” the result of an authentication request. It can be {ok,User}, 403 or {401,Realm} πŸ‘€
  • Time β€” how long it took to serve the request, in microseconds ⏱️

For every single one of these callbacks: ServerName is the name of the virtual server, Type is the atom access or auth, and State is the internal state of the logger. iykyk πŸ’…


  • shaper = Module β€” picks a module to control access to this virtual server 🚦 Access can be gated on the client's IP address, and you can also throttle HTTP requests based on how fast the client is downloading. This module must implement the yaws_shaper behaviour. There is no such module configured by default, bestie β€” you gotta bring your own.
  • dir_listings = true | true_nozip | false β€” set this to false and Yaws is not gonna do its automatic dir listing thing anymore. Instead it sends a 403 Forbidden status code, periodt. Set it to true_nozip to dodge the auto-generated all.zip entries. Default is false.
  • extra_cgi_vars = ..... β€” adds extra CGI or FastCGI variables, slay. For example:
<extra_cgi_vars dir='/path/to/some/scripts'>
  var = val
  ...
</extra_cgi_vars>
  • statistics = true | false β€” turns statistics gathering for a virtual server on or off. Default is false.
  • fcgi_app_server = Host:Port β€” the hostname and TCP port number of a FastCGI application server 🌸 Wanna give an IPv6 address? Pop it inside square brackets (ex: "[::1]:9000"). The TCP port number is not optional, no exceptions. There is no default value.
  • fcgi_trace_protocol = true | false β€” turns tracing of FastCGI protocol messages on or off, logged as info log messages. Disabled by default.
  • fcgi_log_app_error = true | false β€” turns logging of application error messages on or off (that's output to stderr and a non-zero exit value). Disabled by default.
  • deflate = true | false β€” turns deflate compression on or off for a server. Default is false.
  • <deflate> ... </deflate> β€” this opens and closes the deflate compression configuration for this server πŸŽ€ The stuff below is what's allowed between a matching pair of <deflate> and </deflate> delimiters.
    • min_compress_size = nolimit | Integer β€” sets the smallest response size that'll get compressed. If you're not using nolimit, the value you give must be strictly positive. Default is nolimit.
    • compression_level = none | default | best_compression | best_speed | 0..9 β€” sets the compression level, bestie. 0 (none) gives no compression at all, 1 (best_speed) gives you the best speed, and 9 (best_compression) gives you the best compression. Default is default.
    • window_size = 9..15 β€” sets the zlib compression window size. It's gotta be in the range 9 through 15. Bigger values here = better compression, but it costs you memory. Default is 15.
    • mem_level = 1..9 β€” says how much memory should be allocated for the internal compression state 🧠 mem_level=1 uses the least memory but it's slow and it drops the compression ratio; mem_level=9 uses the most memory for optimal speed. Default is 8.
    • strategy = default | filtered | huffman_only β€” this one tunes the compression algorithm. Peep zlib(3erl) for more deets on the strategy parameter. Default is default.
    • use_gzip_static = true | false β€” if true, Yaws will try to serve precompressed versions of static files πŸ‘‘ It looks for precompressed files in the same location as the originals, and they have to end in ".gz". Only files that don't fit in the cache are concerned. Default is false.
    • mime_types = ListOfTypes | defaults | all β€” restricts deflate compression to particular MIME types. The special value all turns it on for every type (it's a synonym of */*) ✨ MIME types in ListOfTypes must have the form type/subtype or type/* (which means all subtypes of that type). Example moment:
mime_types = default image/*
mime_types = application/xml application/xhtml+xml application/rss+xml

By default, these MIME types get compressed (if deflate is set to true): text/*, application/rtf, application/msword, application/pdf, application/x-dvi, application/javascript. And yes, you can stack multiple mime_types directives. Go off πŸ’…

  • docroot = Directory ... β€” this makes the server serve all its content from Directory 🏠 You can pass a space-separated list of directories as the docroot, and if you do, the various directories get searched in order for the requested file. This also works with the ssi and yssi constructs, where the full list of directories gets searched for files to ssi/yssi include. Multiple docroot directives can be used. You need at least one valid docroot β€” invalid docroots are skipped along with their associated auth structures.
  • auth_skip_docroot = true | false β€” if true, the docroot won't be searched for .yaws_auth files. So useful when the docroot is huge and searching it at Yaws startup is just, like, prohibitive 😭 Defaults to false.
  • partial_post_size = Integer | nolimit β€” when a Yaws file receives large POSTs, this parameter decides how much data comes in with each chunk. The default value is 10240.
⚠️ Setting it to nolimit is potentially dangerous. Don't say I didn't warn you.
  • dav = true | false β€” turns on the DAV protocol for this server. Heads up: the dav support in Yaws is highly limited πŸ’€ If dav is turned on, .yaws processing of .yaws pages is turned off. Default is false. The socket read timeout comes from the keepalive_timeout setting. If the read isn't done within the timeout, the POST will fail.
  • tilde_expand = true|false β€” if this is set to false, Yaws will never do tilde expansion. The default is false. So what is tilde expansion, you ask? It's the mechanism where a URL of the form http://www.foo.com/~username turns into a request where the docroot for that particular request is set to the directory ~username/public_html/ πŸ¦‹
  • allowed_scripts = ListOfSuffixes β€” the allowed script types for this server. The recognized ones are yaws, cgi, fcgi, php. Default is allowed_scripts = yaws php cgi fcgi.
πŸ“Œ Note: for fcgi scripts, the FastCGI application server only gets called if a local file with the .fcgi extension exists. But β€” plot twist β€” the contents of that local .fcgi file are ignored.
  • tilde_allowed_scripts = ListOfSuffixes β€” the allowed script types for this server when executing files in a user's public_html folder. Recognized are yaws, cgi, fcgi, php. Default is tilde_allowed_scripts = β€” i.e. empty.
  • index_files = ListOfResources β€” this sets the list of resources to look for when a directory is requested by the client πŸ‘€ If the last entry begins with a /, and none of the earlier resources are found, Yaws will perform a redirect to that uri. Default is index_files = index.yaws index.html index.php.
  • appmods = ListOfModuleNames β€” if any of the names in ListOfModuleNames show up as components in the path for a request, the path request parsing stops right there and that module gets called πŸ”₯ There's also an alternate syntax for specifying appmods when we don't want our internal erlang module names exposed in the URL paths. We can specify:
appmods = <Path1, Module1> <Path2, Modules2> ...

Say, for example, we have the URL http://www.example.org/myapp/foo/bar/baz?user=joe while we have the module foo defined as an appmod β€” then the function foo:out(Arg) gets invoked instead of searching the filesystems below the point foo. The Arg argument will have the missing path part supplied in its appmoddata field.

You can also exclude certain directories from appmod processing. This is particularly interesting for '/' appmods. Example:

appmods = </, myapp exclude_paths icons js top/static>

That config up there invokes the 'myapp' erlang module on everything except any file found in the directories 'icons', 'js' and 'top/static' relative to the docroot. Vibe check passed ✨

  • dispatchmod = DispatchModule β€” sets DispatchModule as a server-specific request dispatching module. Yaws expects DispatchModule to export a dispatch/1 function. When it receives a request, Yaws hands an #arg{} record to the dispatch module's dispatch/1 function, which returns one of these atom results:
    • done β€” this says the dispatch module handled the request itself and already sent the response, so Yaws should get back to watching for new requests on the connection
    • closed β€” same as done, but the DispatchModule also closed the connection
    • continue β€” the dispatch module has decided not to handle the request and instead wants Yaws to do its regular request dispatching
🫢 Note: when DispatchModule handles a request itself, Yaws does not support tracing, does not increment statistics counters, and does not allow traffic shaping for that request. It does, however, still keep track of maximum keepalive uses on the connection.
  • errormod_404 = Module β€” you can set a special module that handles 404 Not Found messages 😭 The function Module:out404(Arg, GC, SC) will be invoked. The arguments are:
    • Arg β€” a #arg{} record
    • GC β€” a #gconf{} record (defined in yaws.hrl)
  • SC β€” a #sconf{} record (defined in yaws.hrl)

The function can and must do the same things that a normal out/1 does. same energy, same responsibilities πŸ’…


  • errormod_401 = Module β€” okay so you can totally set a special module to handle those 401 Unauthorized moments πŸ’€. Think: instead of just vibing with a boring error, you can show a whole login page instead. Iconic. The function Module:out401(Arg, Auth, Realm) gets invoked, and the args are:
    • Arg β€” a #arg{} record
    • Auth β€” a #auth{} record
    • Realm β€” a string

And yeah, this function can and must do the same things that a normal out/1 does. no cap.

  • errormod_crash = Module β€” you can also drop in a special module that handles the HTML generation of server crash messages 🫢. The default is to display the entire formatted crash message right in the browser, which is honestly great for debugging but not it for production, bestie.

The function Module:crashmsg(Arg, SC, Str) gets called. Str is the real crash message, formatted as a string. This function must return {content,MimeType,Cont} or {html, Str} or {ehtml, Term} β€” and that data will be shipped straight to the client. slay.

  • expires = ListOfExpires β€” this one controls the setting of the Expires HTTP header and the max-age directive of the Cache-Control HTTP header in server responses, but only for specific MIME types ✨. The expiration date can be set:
    • relative to the time the source file was last modified
    • as the time of the client access
    • or as always, in order to empty the cache altogether 🧹

ListOfExpires is defined like this:

expires = <MimeType1, access+Seconds> <MimeType2, modify+Seconds> <MimeType3, always> ...

A MimeType can also have a wildcard as subtype, or both as subtype and type, like type/* or */* πŸ‘‘

These HTTP headers are literally an instruction to the client about the document's validity and persistence. If it's cached, the document may be fetched from the cache rather than from the source until that time has passed. After that, the cached copy is considered "expired" and invalid, and a brand new copy must be obtained from the source. fr fr.

Here's an example:

expires = <image/gif, access+2592000> <image/png, access+2592000>
expires = <image/jpeg, access+2592000> <text/css, access+2592000>
expires = <text/*, always>

and here's another one:

expires = <*/*, always>
  • arg_rewrite_mod = Module β€” you can install a module that rewrites all the Arg #arg{} records at an early stage in the Yaws server 🌸. This is giving "check a cookie, rewrite paths, do lil bits of magic" energy. An arg_rewrite_mod must export an arg_rewrite/1 function that takes and returns an #arg{} record. If the function wants to return a response, it must set the #arg.state field of its return value to an instance of the #rewrite_response{} record.

The module yaws_vdir can be used when you want to serve static content that isn't located in your docroot πŸ‘€. Peep the example at the bottom of this man page to see how to use the opaque + vdir elements to tell the yaws_vdir module what paths to rewrite.

  • start_mod = Module β€” defines a user-provided callback module πŸŽ€. At startup of the server, Module:start/1 will be called. The #sconf{} record (defined in yaws.hrl) is used as the input argument. This lets a user application synchronize its startup with the Yaws server, plus get hold of user-specific configuration data β€” see the explanation for the <opaque> context.
  • revproxy = Prefix Url [intercept_mod Module] β€” make Yaws a reverse proxy 🌈. Prefix is a path inside our own docroot and Url is a URL pointing to a website we want to "mount" under the Prefix path. Like this:
revproxy = /tmp/foo http://www.example.org

which makes that example website appear under /tmp/foo. Iconic behavior honestly.

And yes, you can have multiple reverse proxies inside the same server πŸ‘‘.

You can optionally configure an interception module for each reverse proxy, letting your app examine and modify requests and HTTP headers as they pass through the proxy from client to backend server β€” and also examine and modify responses and HTTP headers as they come back from the backend server through the proxy to the client πŸ”₯.

You specify an interception module by including the optional intercept_mod keyword followed by Module, which should be the name of your interception module.

An interception module is expected to export two functions: rewrite_request/2 and rewrite_response/2. The two arguments passed to the rewrite_request/2 function are a #http_request{} record and a #headers{} record, whereas rewrite_response/2 takes a #http_response{} record and also a #headers{} record. Definitions for these record types live in the yaws_api.hrl header file. Each function can examine each record instance and can either return each original instance or return a modified copy of each instance in its response. The rewrite_request/2 function should return a tuple of the following form:

{ok, #http_request{}, #headers{}}

and the rewrite_response/2 function should similarly return a tuple of the following form:

{ok, #http_response{}, #headers{}}

A #headers{} record can easily be manipulated in an interceptor using the functions listed below:

yaws_api:set_header/2, yaws_api:set_header/3
yaws_api:get_header/2, yaws_api:get_header/3
yaws_api:delete_header/2

Any failures in your interception module's functions will result in HTTP status code 500, indicating an internal server error 😭. so like, be careful.

  • fwdproxy = true|false β€” make Yaws a forward proxy πŸ’œ. By enabling this option you can use Yaws as a proxy for outgoing web traffic, typically by configuring the proxy settings in a web-browser to explicitly target Yaws as its proxy server. cute, right?
  • servername = Name β€” if we're virthosting several servers and want to force a server to match specific Host: headers, we can do this with the "servername" directive ✨. This name doesn't necessarily have to be the same as the name inside <server Name> in certain NAT scenarios. Rarely used feature, ngl.
  • serveralias = ListOfNames β€” this directive sets the alternate names for a virtual host 🌸. A server alias may contain wildcards:
    • '*' matches any sequence of zero or more characters
    • '?' matches one character unless that character is a period ('.')

Multiple serveralias directives may be used. Here's an example:

<server server.domain.com>
  serveralias = server server2.domain.com server2
  serveralias = *.server.domain.com *.server?.domain.com
  ...
</server>
  • php_handler = <Type, Spec> β€” set a handler to interpret .php files 🐘. It can be one of the following definitions:
    • php_handler = <cgi, Filename> β€” the name of (and possibly path to) the php executable used to interpret php scripts (if allowed).
    • php_handler = <fcgi, Host:Port> β€” use the specified fastcgi server to interpret .php files (if allowed).
Yaws does not start the PHP interpreter in fastcgi mode for you. To run PHP in fastcgi mode, call it with the -b option. For example:
php5-cgi -b '127.0.0.1:54321'
This starts a php5 in fastcgi mode listening on the local network interface. To make use of this PHP server from Yaws, specify:
php_handler = <fcgi, 127.0.0.1:54321>
If you need to specify an IPv6 address, use square brackets:
php_handler = <fcgi, [::1]:54321>
The PHP interpreter needs read access to the files it is to serve 🫢. So if you run it in a different security context than Yaws itself, make sure it has access to the .php files. And please note: anyone who is able to connect to the php fastcgi server directly can use it to read any file to which it has read access. You should consider this when setting up a system with several mutually untrusted instances of php πŸ’€. that's a big deal bestie.
  • php_handler = <extern, Module:Function | Node:Module:Function> β€” use an external handler, possibly on another node, to interpret .php files (if allowed).
To interpret a .php file, the function Module:Function(Arg) will be invoked (evaluated inside a rpc call if a Node is specified), where Arg is an #arg{} record. The function must do the same things that a normal out/1 does.

Default value is <cgi, "/usr/bin/php-cgi">. periodt.

  • phpfcgi = Host:Port β€” this target is deprecated πŸ™ˆ. use the 'php_handler' target in the server part instead. Using this directive is the same as: php_handler = <fcgi, Host:Port>.
  • default_type = MimeType β€” overloads the global default_type value for this virtual server πŸ’–.
  • default_charset = Charset β€” overloads the global default_charset value for this virtual server πŸ’–.
  • mime_types_file = File β€” overloads the global mime_type_file value for this virtual server. Mappings defined in File will not overload those defined by add_types directives in the global part πŸ‘€.
  • add_types = ListOfTypes β€” overloads the global add_types values for this virtual server ✨. If a mapping is defined in the global part and redefined in a server part using this directive, then it is replaced. Else it is kept.
  • add_charsets = ListOfCharsets β€” overloads the global add_charsets values for this virtual server ✨. If a mapping is defined in the global part and redefined in a server part using this directive, then it is replaced. Else it is kept.
  • nslookup_pref = [inet | inet6] β€” for fcgi servers and revproxy URLs, define the name resolution preference 🌈. For example, to perform only IPv4 name resolution, use [inet]. To do both IPv4 and IPv6 but try IPv6 first, use [inet6, inet]. Default value is [inet].
  • <ssl> ... </ssl>

okay so this is the little velvet rope that opens and closes an SSL config for one specific server πŸ’… β€” and bb, the SSL tea is real.

You can virthost several SSL servers on the same IP/Port. Here's the twist though: if SNI support is disabled or just plain not supported, those servers have to share the exact same certificate configuration. Which is a whole mess, because a certificate is typically bound to a domainname in the common name part of the certificate, so sharing one is... awkward. 😭

One solution to this problem is to have a certificate with multiple subjectAltNames β€” iconic, flexible, works.

But if SNI support is enabled, then SSL servers on the same IP/Port can each have their own SSL configuration, with a totally different SSL certificate for each one. Like, individual fits for everyone πŸ‘‘. See the global sni directive for that.

Also, quick history lesson because I know you love lore: SNI support was introduced in the SSL application in Erlang/OTP 18.0. It's an extension to the TLS protocol (RFC 4366), which allows the client to include the requested hostname in the first message of its SSL handshake. First message! It's giving main character energy 🌟.

See also http://wiki.cacert.org/VhostTaskForce#Interoperability_Test for browser compatibility. (iykyk πŸ¦‹)


  • keyfile = File β€” says which file contains the private key for the certificate. If you don't specify it, then the certificate file gets used instead. Defaults are cute like that. πŸŽ€
  • certfile = File β€” says which file contains the certificate for the server. Yeah. That's the one. ✨
  • cacertfile = File β€” a file holding trusted certificates, used during client authentication and used when attempting to build the server certificate chain. This same list also gets used in the list of acceptable client CAs that's passed to the client when a certificate is requested. Multi-purpose file, we stan. πŸ’–
  • dhfile = File β€” a file containing PEM-encoded Diffie-Hellman parameters, to be used by the server if a cipher suite using Diffie-Hellman key exchange is negotiated. Not specified? Then default parameters are used. 🧚
  • verify = verify_none | verify_peer β€” sets the level of verification the server does on client certs. Setting verify_none means the x509 validation will be skipped (and no certificate request is sent to the client). Setting verify_peer means a certificate request is sent to the client (and x509 validation is performed).

You might want to use fail_if_no_peer_cert in combination with verify_peer. πŸ‘€

  • fail_if_no_peer_cert = true | false β€” if verify is set to verify_peer and this is set to true, the connection will fail if the client does not send a certificate (i.e. an empty certificate). If set to false, the server will fail only if an invalid certificate is supplied β€” because an empty certificate is considered valid in that case. πŸ’€
  • depth = Int β€” specifies the depth of certificate chains the server is prepared to follow when verifying client certs. For the OTP new SSL implementation it is also used to specify how far the server (Yaws, in our case) shall follow the SSL certificates we present to the clients. Hence, using self-signed certs, we typically need to set this to 0. 🌸
  • password = String β€” if the private key is encrypted on disc, this password is the 3DES key to decrypt it. πŸ—οΈ
  • ciphers = String β€” this string specifies the SSL cipher string. The syntax of the SSL cipher string is a 4-tuple representation of the map returned by ssl:cipher_suites/2,3: {#{key_exchange}, #{cipher}, #{mac}, #{prf}}.
ciphers = "[{dhe_rsa,aes_256_cbc,sha,default_prf}, \
            {dhe_dss,aes_256_cbc,sha,default_prf}]"

In older versions of Yaws, a cipher tuple lacked the #{prf} element. When Yaws reads a cipher of the old format from configuration, it attempts to convert it to a 4-tuple by adding default_prf for the #{prf} element. Be aware that this may not work for all ciphers; if it fails, manual intervention is needed to properly configure the ciphers in the new format. πŸ™ˆ

  • eccs = String β€” this string specifies the supported Elliptic Curve Cryptography (ECC). It must be a subset of ssl:eccs(). For PCI DSS compliance β€” which is the main reason why you would want to change this β€” set it on a single line to:
eccs = "[sect571r1, sect571k1, secp521r1, brainpoolP512r1, \
         sect409k1, sect409r1, brainpoolP384r1, secp384r1, \
         sect283k1, sect283r1, brainpoolP256r1, secp256k1, \
         secp256r1, sect239k1, xsect233k1, sect233r1, \
         secp224k1, secp224r1]"
  • secure_renegotiate = true | false | undefined β€” Not supported for Erlang/OTP version 29.0 or newer. This specifies whether to reject a renegotiation attempt that does not live up to RFC 5746. By default secure_renegotiate is set to false for protocol versions that support it, i.e. secure renegotiation will be used if possible but it will fallback to unsecure renegotiation if the peer does not support RFC 5746. Set it to undefined to use the ssl module default setting, to avoid errors with protocol versions that don't support it, such as TLS version 1.3. For more details, see the ssl manual page at http://www.erlang.org/doc/man/ssl.html πŸ¦‹
  • client_renegotiation = true | false | undefined β€” enables or disables the Erlang/OTP SSL application client renegotiation option. Defaults to true for protocol versions that support it. Set it to undefined to use the ssl module default setting, to avoid errors with protocol versions that don't support it, such as TLS version 1.3. For more details, see the ssl manual page at http://www.erlang.org/doc/man/ssl.html ✨
  • honor_cipher_order = true | false β€” if true (the default), use the server's preference for cipher selection. If false, use the client's preference. Pick your side, bestie. πŸ‘‘
  • protocol_version = ProtocolList β€” specifies the list of SSL protocols that will be supported. If not set, it defaults to all protocols supported by the erlang ssl application. For example, to support only TLS versions 1.3, 1.2, 1.1, and 1:
protocol_version = tlsv1.3, tlsv1.2, tlsv1.1, tlsv1
  • require_sni = true | false β€” if true, the server will reject non-SNI clients and clients providing an unknown SNI hostname (this last remark is only relevant for the first virtual server of an SSL group). This directive is ignored if SNI support is disabled (or not supported). Default is false. 🚫

πŸ”€ <redirect> ... </redirect> πŸ”€

Defines a redirect mapping. The following items are allowed within a matching pair of <redirect> and </redirect> delimiters.

We can have a series of redirect rules in one of the formats below:

Path = URL
Path = code
Path = code URL

Path must be an url-decoded path beginning with a slash. URL may be either a relative URL (a path beginning with a slash), or an absolute URL. In the first case, the scheme:hostname:port of the current server will be added. All accesses to Path will be redirected to URL/Path (or scheme:hostname:port/URL/Path if URL is relative). URL must be url-encoded. πŸ“ Note that the original path is appended to the redirected URL.

For example, assume we have the following redirect configuration:

<redirect>
  /foo = http://www.mysite.org/zapp
  /bar = /tomato.html
</redirect>

Assuming this config resides on a site called http://abc.com, we have the following redirects:

http://abc.com/foo -> http://www.mysite.org/zapp/foo
http://abc.com/foo/test -> http://www.mysite.org/zapp/foo/test
http://abc.com/bar -> http://abc.com/tomato.html/bar
http://abc.com/bar/x/y/z -> http://abc.com/tomato.html/bar/x/y/z

By default, Yaws will perform a 302 redirect. The HTTP status code can be changed using the code parameter. Note that the status code must be known by Yaws. ✨

  • For 3xx status codes, the URL parameter must be present and will be used to build the new location.
  • For other status codes (1xx, 2xx, 4xx and 5xx), it can be omitted. In the absence of URL, Yaws will return a generic response with the specified status code.
  • Otherwise, the URL parameter must be a relative URL and will be used to customize the response.

Sometimes we do not want to have the original path appended to the redirected path. 😀 To get that behaviour we specify the config with == instead of =.

<redirect>
  /foo == http://www.mysite.org/zapp
  /bar = /tomato.html
</redirect>

Now a request for http://abc.com/foo/x/y/z simply gets redirected to http://www.mysite.org/zapp. This is typically used when we simply want a static redirect at some place in the docroot. So clean, so simple, ate. πŸ’…

When we specify a relative URL as the target for the redirect, the redirect will be to the current http(s) server.


πŸ” <auth> ... </auth> πŸ”

Defines an auth structure. The following items are allowed within a matching pair of <auth> and </auth> delimiters.

  • docroot = Docroot β€” if a docroot is defined, this auth structure will be tested only for requests in the specified docroot. No docroot configured means all docroots. And here's the plot twist 🌈: if two auth structures are defined, one with a docroot and one with no docroot, the first of both overrides the second one for requests in the configured docroot.
  • dir = Dir β€” makes Dir be controlled by WWW-authenticate headers. In order for a user to have access to a WWW-Authenticate controlled directory, the user must supply a password. πŸ”‘ The Dir must be specified relative to the docroot. Multiple dir can be used. If no dir is set, the default value, "/", will be used.

realm = Realm

In whatever directory you drop this in, the WWW-Authenticate Realm gets set to this value. It's literally just labeling the little login box the browser pops up. ✨


authmod = AuthMod

If you define an auth module here, then AuthMod:auth(Arg, Auth) gets called for every single access to this directory. No skips, no shortcuts. The auth/2 function is supposed to hand back one of these:

  • true
  • false
  • {false, Realm}
  • {appmod, Mod}

And if it hands back {appmod, Mod}, then Yaws hits up Mod:out401(Arg, Auth, Realm) to actually deliver the content. But heads up: if errormod_401 is defined, that call to Mod just gets ignored. Also, Mod:out(Arg) is deprecated β€” don't be that girl. 😭

So what's this even for? Like, cookie auth, bestie. The auth() callback would peek and see if a valid cookie header is hanging around; if it's not there, it returns {appmod, ?MODULE} and then the out401/1 function in that same module returns {redirect_local, "/login.html"}. Clean. Iconic. πŸ’…


user = User:Password | "User:{Algo}Hash" | "User:{Algo}$Salt$Hash"

Inside this directory, the user User gets access if they hand over the password Password in that popup dialogue the browser throws at them. You can also supply a hashed password, encoded in base64 β€” and in that case you must set the algorithm used to hash it. Algo has to be one of these, no substitutions:

md5 | ripemd160 | sha | sha224 | sha256 | sha384 | sha512

Salted hashes? Absolutely allowed, queen. If you go that route, the Salt must be provided, encoded in base64. And yes β€” you can stack multiple users inside a single <auth> </auth> pair. Go off. πŸŽ€


pam service = pam-service

If the item pam shows up as part of the auth structure, Yaws will also try to authenticate the user using "pam", with the pam service you named. The usual suspects live under /etc/pam.d, and the usual values are stuff like "system-auth" etc.

⚠️ pam authentication is done by an Erlang port program which is typically installed as suid root by the Yaws install script. Just so you know. πŸ‘‘

allow = all | ListOfHost

The allow directive controls which hosts can get into an area of the server. You can lock it down by IP address or IP address range. If you say all, then everybody's allowed in β€” subject to however the deny and order directives are set up. To allow only particular hosts or groups of hosts, you can write the host in any of these formats:

  • A full IP address
allow = 10.1.2.3
allow = 192.168.1.104, 192.168.1.205
  • A network/netmask pair
allow = 10.1.0.0/255.255.0.0
  • A network/nnn CIDR specification
allow = 10.1.0.0/16

deny = all | ListOfHost

This one lets you restrict access to the server based on IP address. The arguments are identical to the arguments for the allow directive β€” same formats, same energy, just the opposite vibe. 🚫


order = Ordering

The order directive, together with the allow and deny directives, runs a whole three-pass access control system. Listen, it's giving bouncer at the club:

  1. First pass processes either all the allow or all the deny directives β€” whichever one the order directive names.
  2. Second pass parses the rest of the directives (so, deny or allow).
  3. Third pass handles all the requests that didn't match either of the first two.

Ordering is one of these (and the default value is deny,allow):

  • allow,deny β€” First, all allow directives get evaluated; at least one must match, or the request is rejected. Next, deny directives get evaluated β€” if any of them match, the request is rejected. Last, any requests that didn't match an allow or a deny directive are denied by default.
  • deny,allow β€” First, all deny directives get evaluated; if any matched, the request is denied unless it also matches an allow directive. Any requests that don't match any allow or deny directives are permitted.

<opaque> ... </opaque>

This begins and ends an opaque configuration context for this server, where you can write Key = Value directives. Yaws itself ignores these (hence the name, opaque), but they can be read as a list of tuples {Key,Value} stored in the #sconf.opaque record entry. Peep the description of the start_mod directive too. πŸ‘€

This whole mechanism exists so a surrounding application can pass data down into the individual .yaws pages. Sneaky. Useful. Iconic. πŸ¦‹


strip_undefined_bindings = true | false

This changes how the {bindings, [...]} directive behaves. Set it and all the undefined keys found in returned out/1 content get treated as if they were defined with an empty value β€” meaning all those undefined bindings get effectively stripped right out of the returned content. By default strip_undefined_bindings is false, which means undefined bindings are just ignored and their text is left exactly as-is in the returned content.

Heads up: this setting applies only to out/1 content. Not to static pages, not to other returned content. ✨

<extra_response_headers> ... </extra_response_headers>

This begins and ends a configuration context for extra response headers on this server, where you can specify directives for adding headers, erasing headers, and modules for handling extra response headers, like so:

  • add Hdr = Value β€” Add Hdr with value Value to the response, but only if the response status code is one of these values:
    • 200 OK
    • 201 Created
    • 204 No Content
    • 206 Partial Content
    • 301 Moved Permanently
    • 302 Found
    • 303 See Other
    • 304 Not Modified
    • 307 Temporary Redirect
    • 308 Permanent Redirect

For any other status code, Hdr is not added. Periodt.

  • always add Hdr = Value β€” Unconditionally add Hdr with value Value to the response, regardless of the response status code. No rules, no conditions. πŸ’…
  • erase Hdr β€” Remove Hdr and its associated value from the response.
  • extramod = Module β€” Specifies a module to call to process extra response headers. Yaws calls Module:extra_response_headers/3 passing the following arguments:
    • Response headers β€” An Erlang map holding the response headers, with header name strings as keys and strings as header values.
    • Arg β€” An #arg{} record representing the request. In cases where an extramod module is called after the invocation of an appmod, the #arg{} record field appmod_name tells you the name of the appmod that serviced the request, which lets the extramod return extra HTTP headers that are appropriate for that particular appmod.
    • {StatusCode,Version} β€” A tuple where StatusCode is the numeric HTTP status code for the response, and Version is a tuple specifying the HTTP version, e.g. {1,1} for HTTP 1.1.

The Module:extra_response_headers/3 function should return either the original header map or a modified map where headers have been added, changed, or deleted. Added headers are not subject to the status code restrictions that the add extra response header directive has β€” but the function can call yaws_api:http_extra_response_headers_add_status_codes/0 to grab the list of status codes for which adding headers is normally allowed. 🧚

For response headers that can have multiple settings β€” like Set-Cookie β€” you can specify multiple values in the extra response header map by using a value of {multi, [Value]}, where [Value] is a list of one or more header values. Set-Cookie is the standard special case: Yaws converts a multi header into a separate Set-Cookie header for each value. For other headers, Yaws converts a multi header into a single HTTP header with a comma-separated value.

🌸 Note that extra response headers do not apply to responses returned directly by any DispatchModule.

options_asterisk_methods = Methods

Set options_asterisk_methods to a comma-separated list of HTTP Methods and Yaws will respond to an OPTIONS request that specifies a literal * as its target with a 200 OK status and an Allow header listing the Methods you specified. And if you don't explicitly set options_asterisk_methods in the config, Yaws defaults to responding to OPTIONS * requests with a 200 OK status and an Allow header listing these HTTP methods:

GET, HEAD, POST, PUT, DELETE, OPTIONS

RFC 7231 section 4.3 lists the standard HTTP method names right here:

GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE

An options_asterisk_methods setting can include any of these HTTP method names, plus PATCH. Now PATCH is not mentioned in RFC 7231 but Yaws supports it anyway, we love an overachiever. ✨ Yaws does not implement CONNECT, but it still supports it inside options_asterisk_methods, because it's totally possible to implement support for it using a dispatchmod. Clever.

And if options_asterisk_methods is set to an empty value, Yaws responds to OPTIONS * requests with status 400 Bad Request. πŸ’€

🌸 EXAMPLES 🌸

Okay bestie, this is the part you actually came for β€” the receipts. ✨ All the little conf-file situations you might find yourself in, and exactly how they look. Let's go. πŸ’…


πŸŽ€ One server, port 80, that's it

The following example defines a single server on port 80.

logdir = /var/log/yaws
<server www.mydomain.org>
    port = 80
    listen = 192.168.128.31
    docroot = /var/yaws/www
</server>

πŸ¦‹ Two servers, same IP, twin energy

And this example shows a similar setup but two web servers on the same IP address.

logdir = /var/log/yaws
<server www.mydomain.org>
    port = 80
    listen = 192.168.128.31
    docroot = /var/yaws/www
</server>

<server www.funky.org>
    port = 80
    listen = 192.168.128.31
    docroot = /var/yaws/www_funky_org
</server>

πŸ” www-authenticate and zero access logging

An example with www-authenticate and no access logging at all.

logdir = /var/log/yaws
<server www.mydomain.org>
    port = 80
    listen = 192.168.128.31
    docroot = /var/yaws/www
    access_log = false
    <auth>
        dir = secret/dir1
        realm = foobar
        user = jonny:verysecretpwd
        user = benny:thequestion
        user = ronny:havinganamethatendswithy
   </auth>
</server>

🌈 A user-defined module at startup + your own lil config corner

An example specifying a user defined module to be called at startup, as well as some user specific configuration.

<server www.funky.org>
    port = 80
    listen = 192.168.128.31
    docroot = /var/yaws/www_funky_org
    start_mod = btt
    <opaque>
            mydbdir = /tmp
            mylogdir = /tmp/log
    </opaque>
</server>

πŸ‘‘ GSSAPI/SPNEGO, the spicy auth glow-up

An example specifying the GSSAPI/SPNEGO module (authmod_gssapi) to be used for authentication. This module requires egssapi version 0.1~pre2 or later available at http://www.hem.za.org/egssapi/.

The Kerberos5 keytab is specified as 'keytab = File' directive in opaque. This keytab should contain the keys of the HTTP service principal, 'HTTP/www.funky.org' in this example.

<server www.funky.org>
    port = 80
    listen = 192.168.128.31
    docroot = /var/yaws/www_funky_org
    start_mod = authmod_gssapi
    <auth>
            authmod = authmod_gssapi
            dir = secret/dir1
    </auth>
    <opaque>
            keytab = /etc/yaws/http.keytab
    </opaque>
</server>

✨ The main-character example: two servers, one SSL, different IPs

And finally a slightly more complex example with two servers on the same IP, and one SSL server on a different IP.

When there are more than one server on the same IP, and they have different names the server must be able to choose one of them if the client doesn't send a Host: header. Yaws will choose the first one defined in the conf file. 🫢

logdir = /var/log/yaws
max_num_cached_files = 8000
max_num_cached_bytes = 6000000

<server www.mydomain.org>
    port = 80
    listen = 192.168.128.31
    docroot = /var/yaws/www
</server>

<server www.funky.org>
    port = 80
    listen = 192.168.128.31
    docroot = /var/yaws/www_funky_org
</server>

<server www.funky.org>
    port = 443
    listen = 192.168.128.32
    docroot = /var/yaws/www_funky_org
    <ssl>
       keyfile = /etc/funky.key
       certfile = /etc/funky.cert
       password = gazonk
    </ssl>
</server>

πŸ’œ vdirs, aka virtual directories, aka paths that lie (affectionately)

Finally an example with virtual directories, vdirs.

<server server.domain>
    port = 80
    listen = 192.168.128.31
    docroot = /var/yaws/www
    arg_rewrite_mod = yaws_vdir
    <opaque>
        vdir = "/virtual1/ /usr/local/somewhere/notrelated/to/main/docroot"
        vdir = "/myapp/ /some/other/path can include/spaces"
        vdir = "/icons/  /usr/local/www/yaws/icons"
    </opaque>
</server>

The first defined vdir can then be accessed at or under http://server.domain/virtual1/ or http://server.domain/virtual1

πŸ’‘ Notice how that second path has literal spaces in it? Yeah, vdir said "I can include spaces, actually." Iconic behaviour. 🌈

🌟 AUTHOR 🌟

Written by Claes Wikstrom


πŸ‘€ SEE ALSO πŸ‘€


Aaaaand that's the whole page, bestie, we did it!! πŸŽ‰πŸ₯³ Go forth and yaws.conf responsibly. This was YAWS.CONF(5) β€” a section 5 manual page (that's file formats, iykyk), from the Yaws source, dated with its very own footer date down at the bottom of the page. So yeah, if you ever need to cite it: it's YAWS.CONF(5), version-and-date included, no cap. πŸ’–πŸ¦‹βœ¨

Now go restart your webserver, king. πŸ‘‘

πŸ“¦ where this page comes from