YAWS.CONF(5)
πΈ 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>.accessgets written containing an access log in NCSA combined/XLF/ELF log format. (peephttp://www.w3.org/TR/WD-logfile.htmlfor more details on Extended Log File Format, iykyk)<Host>.authβ for each virtual host served by Yaws, a file<Host>.authgets 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>.accessand<Host>.authfiles get used only if the directivelogger_modis not set, or is set toyaws_log. and the default value forlogdiris"."π
ebin_dir = Directoryβ addsDirectoryto 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 aDirectoryas 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. theinclude_dirdirectives are used to search for include files. multiplesrc_dirdirectives 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β addsDirectoryto the path of directories where the Erlang compiler searches for include files. we need this if we want to include.hrlfiles 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 is400.max_num_cached_bytes = Integerβ controls the total amount of RAM which can maximally be used for cached RAM files. the default value is1000000, aka 1 megabyte β¨max_size_cached_file = Integerβ sets a maximum size on the files that are RAM cached by Yaws. the default value is8000, 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 mostcache_refresh_secsnumber of seconds β° the default is30. 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 to0. and if the debug flag (-d) is passed to the Yaws start script, this value is automatically set to0. 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 isfalse.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 integerXwill ensure that connections are closed once they've been usedXtimes π«Ά 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 atomundefinedor as a proplist of valid process options. the supported options arefullsweep_after,min_heap_size, andmin_bin_vheap_size, each taking an associated integer value π’ other process options are ignored. the proplist may also be empty. seeerlang:spawn_opt/4for details on these options.large_file_chunk_size = Integerβ sets the chunk size used by Yaws to send large files π¦ the default value is10240.large_file_sendfile = erlang | disableβ sets the version of sendfile method to use to send large files:erlangβ usefile:sendfile/5.disableβ usegen_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 is8. and specifying a value of0effectively 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 toFile.old, is1000000, aka 1 megabyte. this value can be changed. if we set the value to0the 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 to0and 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 istrue. she either goes all in or she doesn't, main character behavior πenable_soap = true | falseβ iftrue, a soap server will be started at startup of Yaws π§Ό default isfalse.soap_srv_mods = ListOfModuleSettingβ ifenable_soapistrue, a startup Yaws will invokeyaws_soap_srv:setup()to setup the modules set here.ModuleSettingis 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 ofyaws_soap_lib:initModel()and then be used as a XML namespace prefix. note, theWsdlFilehere 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 isphp_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 areport.logfile. default value istrue.ysession_mod = Moduleβ allows specifying a different Yaws session storage mechanism instead of an ETS table ποΈ one of the drawbacks of the defaultyaws_session_serverimplementation 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 πͺModuleis expected to provide anew_cookie/0function 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 forysession_idle_timeoutmilliseconds, check all Yaws sessions and remove any that have timed out β³ the defaultysession_idle_timeoutvalue is2*60*1000(2 minutes).ysession_long_timeout = Integerβ controls Yaws session periodic cleanup. everyysession_long_timeoutmilliseconds, check all Yaws sessions and remove any that have timed out π§Ή the defaultysession_long_timeoutvalue is60*60*1000(1 hour).runmod = ModuleNameβ at startup Yaws will invokeModuleName: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 theHostheader 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 theyaws.conffile when there's no name match. If this isfalseand the request has noHostheader at all? Yaws sends back a400 Bad Request, exactly like the HTTP standard demands, no arguing π . In real live hosting you lowkey want thisfalse, but in testing/development it's so convenient to just set ittrue. Default istrue.keepalive_timeout = TimeInMilliseconds | infinityβ if the HTTP session is being kept alive (i.e., not immediately closed), it'll shut itself down afterkeepalive_timeoutmilliseconds, unless a new request shows up in that window. Default value is30000. The valueinfinityis 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 β peepfilelib:wildcard/1for 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 istext/plain.default_charset = Charsetβ defines the default charset that gets added when a response content-type istext/*. By default, no charset is added.mime_types_file = Fileβ overrides the defaultmime.typesfile 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.ListOfTypesis 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.ListOfCharsetsis 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 is8000.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 aHost:field, Yaws will pick the first server specified in the config file. If the specified IP address is0.0.0.0, Yaws will listen on all local IP addresses on the specified port. Default is127.0.0.1. Multiplelistendirectives 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 is1024.<listen_opts> ... </listen_opts>β defines extra options to be set on the listen socket and, by inheritance, on accepted sockets. Peepinet:setopts/2for details. Supported options are:buffer = Integer(default: same asinet:setopts/2)delay_send = true | false(default: same asinet:setopts/2)linger = Integer | false(default: same asinet:setopts/2)nodelay = true | false(default: same asinet:setopts/2)priority = Integer(default: same asinet:setopts/2)sndbuf = Integer(default: same asinet:setopts/2)recbuf = Integer(default: same asinet:setopts/2)send_timeout = Integer | infinity(default: same asinet:setopts/2)send_timeout_close = true | false(default: same asinet: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 thesubconfigdirective of the global part, but here the files should only contain directives allowed in the server part.subconfigdir = Directoryβ same as thesubconfigdirdirective 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 port80, since that port can only be opened by a privileged user. Instead Yaws listens to a high port number,8000, andiptablesare used to redirect traffic to port80over to port8000(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, orstunnel, is sitting in front of Yaws.auth_log = true | falseβ enable or disable the auth log for this virtual server. Default istrue.access_log = true | falseβ setting this directive tofalseturns off traffic logging for this virtual server. The default value istrue.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>.accessand<Host>.authfiles in the configured or defaultlogdir. This module must implement the behaviouryaws_logger. Default value isyaws_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 returnfalse.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 internalNewState.Module:write_log(ServerName, Type, State, Infos)β when it needs to log a message, Yaws will call this function. The parameterInfosis{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{}recordInHdrsβ 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{}recordPathβ the URI path of the request, as a stringItemβ the result of an authentication request. It can be{ok,User},403or{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 theyaws_shaperbehaviour. There is no such module configured by default, bestie β you gotta bring your own.dir_listings = true | true_nozip | falseβ set this tofalseand Yaws is not gonna do its automatic dir listing thing anymore. Instead it sends a403 Forbiddenstatus code, periodt. Set it totrue_nozipto dodge the auto-generatedall.zipentries. Default isfalse.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 isfalse.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 isfalse.<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 usingnolimit, the value you give must be strictly positive. Default isnolimit.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, and9(best_compression) gives you the best compression. Default isdefault.window_size = 9..15β sets the zlib compression window size. It's gotta be in the range9through15. Bigger values here = better compression, but it costs you memory. Default is15.mem_level = 1..9β says how much memory should be allocated for the internal compression state π§mem_level=1uses the least memory but it's slow and it drops the compression ratio;mem_level=9uses the most memory for optimal speed. Default is8.strategy = default | filtered | huffman_onlyβ this one tunes the compression algorithm. Peepzlib(3erl)for more deets on the strategy parameter. Default isdefault.use_gzip_static = true | falseβ iftrue, 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 isfalse.mime_types = ListOfTypes | defaults | allβ restricts deflate compression to particular MIME types. The special valueallturns it on for every type (it's a synonym of*/*) β¨ MIME types inListOfTypesmust have the formtype/subtypeortype/*(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 fromDirectoryπ 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 thessiandyssiconstructs, where the full list of directories gets searched for files to ssi/yssi include. Multipledocrootdirectives 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β iftrue, the docroot won't be searched for.yaws_authfiles. So useful when the docroot is huge and searching it at Yaws startup is just, like, prohibitive π Defaults tofalse.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 is10240.
β οΈ 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 π Ifdavis turned on,.yawsprocessing of.yawspages is turned off. Default isfalse. The socket read timeout comes from thekeepalive_timeoutsetting. If the read isn't done within the timeout, the POST will fail.tilde_expand = true|falseβ if this is set tofalse, Yaws will never do tilde expansion. The default isfalse. So what is tilde expansion, you ask? It's the mechanism where a URL of the formhttp://www.foo.com/~usernameturns 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 areyaws,cgi,fcgi,php. Default isallowed_scripts = yaws php cgi fcgi.
π Note: for fcgi scripts, the FastCGI application server only gets called if a local file with the.fcgiextension exists. But β plot twist β the contents of that local.fcgifile are ignored.
tilde_allowed_scripts = ListOfSuffixesβ the allowed script types for this server when executing files in a user'spublic_htmlfolder. Recognized areyaws,cgi,fcgi,php. Default istilde_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 isindex_files = index.yaws index.html index.php.appmods = ListOfModuleNamesβ if any of the names inListOfModuleNamesshow 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β setsDispatchModuleas a server-specific request dispatching module. Yaws expectsDispatchModuleto export adispatch/1function. When it receives a request, Yaws hands an#arg{}record to the dispatch module'sdispatch/1function, 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 connectionclosedβ same asdone, but theDispatchModulealso closed the connectioncontinueβ 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 handles404 Not Foundmessages π The functionModule:out404(Arg, GC, SC)will be invoked. The arguments are:Argβ a#arg{}recordGCβ a#gconf{}record (defined inyaws.hrl)
- SC β a
#sconf{}record (defined inyaws.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 those401 Unauthorizedmoments π. Think: instead of just vibing with a boring error, you can show a whole login page instead. Iconic. The functionModule:out401(Arg, Auth, Realm)gets invoked, and the args are:- Arg β a
#arg{}record - Auth β a
#auth{}record - Realm β a string
- Arg β a
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 theExpiresHTTP header and themax-agedirective of theCache-ControlHTTP 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 theArg#arg{}records at an early stage in the Yaws server πΈ. This is giving "check a cookie, rewrite paths, do lil bits of magic" energy. Anarg_rewrite_modmust export anarg_rewrite/1function that takes and returns an#arg{}record. If the function wants to return a response, it must set the#arg.statefield 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/1will be called. The#sconf{}record (defined inyaws.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 π.Prefixis a path inside our own docroot andUrlis a URL pointing to a website we want to "mount" under thePrefixpath. 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 specificHost: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.phpfiles π. 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.phpfiles (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.phpfiles (if allowed).
To interpret a.phpfile, the functionModule:Function(Arg)will be invoked (evaluated inside a rpc call if aNodeis specified), whereArgis an#arg{}record. The function must do the same things that a normalout/1does.
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 globaldefault_typevalue for this virtual server π.default_charset = Charsetβ overloads the globaldefault_charsetvalue for this virtual server π.mime_types_file = Fileβ overloads the globalmime_type_filevalue for this virtual server. Mappings defined inFilewill not overload those defined byadd_typesdirectives in the global part π.add_types = ListOfTypesβ overloads the globaladd_typesvalues 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 globaladd_charsetsvalues 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. Settingverify_nonemeans the x509 validation will be skipped (and no certificate request is sent to the client). Settingverify_peermeans 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β ifverifyis set toverify_peerand this is set totrue, the connection will fail if the client does not send a certificate (i.e. an empty certificate). If set tofalse, 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 to0. πΈ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 byssl: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 ofssl: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 defaultsecure_renegotiateis set tofalsefor 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 toundefinedto 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 athttp://www.erlang.org/doc/man/ssl.htmlπ¦client_renegotiation = true | false | undefinedβ enables or disables the Erlang/OTP SSL application client renegotiation option. Defaults totruefor protocol versions that support it. Set it toundefinedto 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 athttp://www.erlang.org/doc/man/ssl.htmlβ¨honor_cipher_order = true | falseβ iftrue(the default), use the server's preference for cipher selection. Iffalse, 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β iftrue, 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 isfalse. π«
π <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
URLparameter 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
URLparameter 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β makesDirbe 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. π TheDirmust be specified relative to the docroot. Multipledircan be used. If nodiris 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:
truefalse{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:
- First pass processes either all the
allowor all thedenydirectives β whichever one theorderdirective names. - Second pass parses the rest of the directives (so,
denyorallow). - 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, allallowdirectives get evaluated; at least one must match, or the request is rejected. Next,denydirectives get evaluated β if any of them match, the request is rejected. Last, any requests that didn't match anallowor adenydirective are denied by default.deny,allowβ First, alldenydirectives get evaluated; if any matched, the request is denied unless it also matches anallowdirective. Any requests that don't match anyallowordenydirectives 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β AddHdrwith valueValueto the response, but only if the response status code is one of these values:200 OK201 Created204 No Content206 Partial Content301 Moved Permanently302 Found303 See Other304 Not Modified307 Temporary Redirect308 Permanent Redirect
For any other status code, Hdr is not added. Periodt.
always add Hdr = Valueβ Unconditionally addHdrwith valueValueto the response, regardless of the response status code. No rules, no conditions. πerase Hdrβ RemoveHdrand its associated value from the response.extramod = Moduleβ Specifies a module to call to process extra response headers. Yaws callsModule:extra_response_headers/3passing 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 anextramodmodule is called after the invocation of an appmod, the#arg{}record fieldappmod_nametells you the name of the appmod that serviced the request, which lets theextramodreturn extra HTTP headers that are appropriate for that particular appmod. {StatusCode,Version}β A tuple whereStatusCodeis the numeric HTTP status code for the response, andVersionis 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
- nixpkgs unstable: yaws 2.3.1
- ubuntu stonking: yaws 2.3.1+dfsg-2 Β· BSD-YAWS
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms