admin-config - NaviServer Configuration Guide
When NaviServer is started, typically a configuration file is provided containing certain settings for the server. The configuration file includes network configuration options, log file information, loading of modules and database drivers and the like. Actually, the configuration file can contain the definition of multiple servers, running concurrently or depending on command line arguments also separately (see admin-maintenance).
The NaviServer configuration file consists of multiple sections containing global configuration of the instance and sections for each server and module. The configuration file is actually a Tcl script, such that at user can set variables, defined procs to avoid repeated patterns and the like. Tcl variables are typically used to certain parameters at the beginning of the file, which might be often changed. This eases the maintenance.
The following sample configuration files might be used as a starting point for site specific configurations:
Several configuration hints and details can be found also in other places in the manual, such as e.g. in admin-tuning or for module specific parameters (which might not be necessary in every installation). Furthermore, the configuration mechanism of NaviServer is extensible, therefore, modules and applications can as well use the configuration file with its abstraction to define application specific parameters. So, a listing of the parameters can never complete. In this page, we primarily define introductory examples for typical usage scenarios.
The main sections of a configuration file are:
Global configuration values
general parameters (starting with "ns/parameters")
thread parameters (starting with "ns/threads")
mimetypes (starting with "ns/mimetypes")
database drivers (starting with "ns/db")
global modules (starting with "ns/modules")
servers (starting with "ns/servers")
Examples for global parameters are in the mentioned sample configuration files and also e.g. in tcl-libraries and admin-tuning.
Server specific parameters (starting with "ns/server/$server")
Module specific parameters (like nssock, nsssl, nscp, nslog, nscgi or nsperm.
In general, when modifying configuration files, it is always a good idea to check whether a configuration file is syntactically correct before starting the server. This can reduce the downtime of a production server in case of typos. A configuration file named "nsd-config.tcl" can be checked with the command line option -T of NaviServer.
/usr/local/ns/bin/nsd -t nsd-config.tcl -T
The global parameters contain the basic setup information, where for example the root of the root of nsd is, where the server log is to be written, or whether the server runs behind a proxy server or not.
ns_section ns/parameters { ns_param home /var/www/myserver/ ns_param tcllibrary tcl ns_param serverlog error.log # ... ns_param reverseproxymode true }
When reverse proxy mode is turned on, the server assumes it is running behind a reverse proxy server. In this case, commands referring to the client IP address will return on default the value as provided by the reverse proxy server (i.e. provided via the x-forwarded-for header field). This will affect the results of ns_conn peeraddr and various introspection commands.
Below, we address some general design considerations when tailoring your one configuration files. Check for simple or real-live setups of larger installations by the provided sample configuration files.
One of the core components of the configuration file are the network drivers: what protocols should be used, on which addresses/ports should be used, or how to set up virtual servers. The most important network drivers of NaviServer are nssock (for HTTP connections) and nsssl (for HTTPS connections).
Several additional network drivers are available via extra modules, such as e.g. nsudp, nssmtpd (for a full list of modules, see the module repository).
In the simplest case, one defines in a configuration file a single server s1 with a single network driver nssock used for plain HTTP connections. In the example below, the server is listening on port 8000.
ns_section ns/servers { ns_param s1 "Server Instance 1" } ns_section ns/server/s1/modules { ns_param http nssock.so } ns_section ns/server/s1/module/http { ns_param address 0.0.0.0 ns_param port 8000 }
In this example, the module nssock is loaded for the server "s1" under the name "http". We show, in later examples, how to load a network driver globally, such that one network driver can be used for multiple servers.
It is as well possible to define multiple servers in the same configuration file (here s1 and s2). These servers use the same driver nsock but with different ports. In this case, it is sufficient to load the driver once.
ns_section ns/servers { ns_param s1 "Server Instance 1" ns_param s2 "Server Instance 1" } # # Server s1 # ns_section ns/server/s1/modules { ns_param http nssock.so } ns_section ns/server/s1/module/http { ns_param address 0.0.0.0 ns_param port 8000 } # # Server s2 # ns_section ns/server/s2/modules { ns_param http nssock.so } ns_section ns/server/s2/module/http { ns_param address 0.0.0.0 ns_param port 8001 }
When the configuration file above is named e.g. two-server-config.tcl, the two servers can be started with a command line like:
/usr/local/ns/bin/nsd -u nsadmin -t two-server-config.tcl -f
When it is the goal to start only one of these servers, one can use e.g. the following command:
/usr/local/ns/bin/nsd -u nsadmin -t two-server-config.tcl -f -server s2
Often, a server has the requirement to listen on multiple addresses, such as on one (or many) IPv4 and one (or many) IPv6 addresses. This can be addressed by simply providing the list of values as a parameter value.
ns_section ns/servers { ns_param s1 "Server Instance 1" } ns_section ns/server/s1/modules { ns_param http nssock.so } ns_section ns/server/s1/module/http { ns_param address "137.208.116.31 2001:628:404:74::31" ns_param port 8000 }
Similarly, we can define a single server, listening on multiple ports. In this case, one can load multiple instances of the driver, where each of the driver listens on a different port.
# # Server s1, using listening on two ports # ns_section ns/server/s1/modules { ns_param http nssock.so } ns_section ns/server/s1/module/http { ns_param address 0.0.0.0 ns_param port "8000 8001" }
When multiple IP addresses and multiple ports are specified, the server will be listening for every specified address on every specified port. In the following example, it will listen on four different combinations of addresses and ports.
ns_section ns/server/s1/module/http { ns_param address "137.208.116.31 2001:628:404:74::31" ns_param port "8000 8001" }
In the last two examples, a single server is listening on different ports and or IP addresses, but the configuration of the driver was otherwise identical. In case, different driver parameters are needed, it is possible to load the same driver multiple times for the same server with different driver names. In the following example, we name the different instances of the network driver http1 and http1.
# # Server s1, using two drivers for listening on two ports on two # different addresses. # ns_section ns/server/s1/modules { ns_param http1 nssock.so ns_param http2 nssock.so } ns_section ns/server/s1/module/http1 { ns_param address 0.0.0.0 ns_param port 8000 } ns_section ns/server/s1/module/http2 { ns_param address 127.0.0.1 ns_param port 8001 }
It would be as well possible to register multiple addresses for every network driver instance (here "http1" and "http2"). In general, by loading a network driver multiple times, all the of parameters of the driver modules (here nssock) can be modified per driver instance.
By using virtual servers, multiple different server configurations can be used while using only a single IP address/port combination. The main different between a virtual server and the case of defining multiple alternative servers above is that the servers are available concurrently. The server determines by the "Host:" header field provided by the client to which server the request should be routed. Using such virtual servers is a common technique, where e.g. for the same IP address, multiple DNS names are registered. According to HTTP/1.1, clients have to send the hostname in the host header field to the server, such that the server can behave differently depending on the contents of this field.
NaviServer can define multiple virtual servers, where each of the servers has a separate setup (e.g., different number of threads defined, different page directories), separate blueprints (e.g., loading of different modules), uses different network drivers, etc. (see Figure 1).
Figure 1: NaviServer Blueprint (per server)
When NaviServer is started, it reads first the configuration file, which contains the definition of all servers, and then, it creates and configures the virtual servers based on this information. This is also the reason, why certain (server-specific) commands cannot be contained (and executed) in the configuration file (e.g., registering URL handlers, callbacks, etc.), but can be loaded from the module files (see also tcl-libraries.html). Additionally, it is possible to specify server specific commands in the section ns/server/SERVERNAME/tcl with the parameter initcmds (see also tcl-libraries.html).
Note that it is also possible to define virtual servers with a single NaviServer server definition, but this will be covered later.
In the following example, we define two separate servers "s1" and "s2", which should act as virtual web servers. This means, we want to define one network driver, which listens on a single port, but which should direct requests to one of the two potentially differently configured servers based on the content of the host header field.
Assume for the IP address of the server the DNS names foo.com, bar.com and baz.com are registered. We define server "s1" and "s2" such that "s1" should receive requests from foo.com, and "s2" should receive requests from bar.com and baz.com. Servers "s1" and "s2" have different pagedir definitions.
For defining virtual servers, the network driver has to be loaded globally (i.e., as module ns/module/http). For requests with missing/invalid host header fields, we have to define a defaultserver to handle such requests in the global definition. In the section ns/module/http/servers we define the mapping between the hostnames and the defined servers.
# # Define two servers s1 and d2 as virtual servers # ns_section ns/servers { ns_param s1 "Virtual Server s1" ns_param s2 "Virtual Server s2 " } ns_section ns/server/s1/fastpath { ns_param pagedir /var/www/s1 } ns_section ns/server/s2/fastpath { ns_param pagedir /var/www/s2 } # # Define a global nssock driver named "http", # directing requests to the virtual servers # based on the "Host:" header field. # # It is necessary to define a "defaultserver" # for requests without a "Host:" header field. # ns_section ns/modules { ns_param http nssock.so } ns_section ns/module/http { ns_param port 8000 ns_param defaultserver s1 } # # Define the mapping between the DNS names and the servers. # ns_section ns/module/http/servers { # # Domain names for s1 # ns_param s1 foo.com # # Domain names for s2 # ns_param s2 bar.com ns_param s2 baz.com }
Note that one can define multiple DNS names also for a single server. With the definition above, "s1" serves foo.com and "s2" serves bar.com and baz.com. Since "s1" is the default server, requests with unregistered hostnames will also be directed to it.
In general, the logic of the definition of servers and network drivers is the same for HTTP (driver nssock) and HTTPS (driver nsssl), except that the latter has some additional configuration parameters, such as e.g. the certificate, or a special configuration of the ciphers, etc.
In order to define virtual servers with HTTPS, one can essentially use the definition of the previous section, and load as well the nsssl driver globally with the name "https", and configure it accordingly.
# # Define a global nsssl driver named https # ns_section ns/modules { ns_param https nsssl.so } ns_section ns/module/https { ns_param port 8433 ns_param defaultserver s1 ns_param certificate /usr/local/ns/modules/https/server.pem } ns_section ns/module/https/servers { ns_param s1 foo.com ns_param s2 bar.com ns_param s2 baz.com }
However, this case requires that all accepted hostnames are listed in the certificate. Such certificates are called multi-domain SAN certificates.
However, there might be cases, where a server listening on a single address has to provide different certificates for e.g. "foo.com" and "bar.com". For virtual hosting, this is a chicken-egg problem: the right certificate is needed at the time the connection is opened, but the virtual server can be only detected while reading the request header.
This is a well-known problem, for which the SNI TLS extension was invented (a hostname that can be used for identifying the certificate is passed during the TLS handshake as well).
In order to configure SNI (Server Name Indication) for HTTPS, one can simply add additional certificates for the server needed. In the following example the default certificate is defined on the level of the global driver (server.pem), whereas the certificate for the server "foo.com" (which will be served by "s1") is defined for the server "s1" separately (foo.com.pem).
# # Define a global nsssl driver named "https" # ns_section ns/modules { ns_param https nsssl.so } ns_section ns/module/https { ns_param port 8433 ns_param defaultserver s1 ns_param certificate /usr/local/ns/modules/https/server.pem } ns_section ns/module/https/servers { ns_param s1 foo.com ns_param s2 bar.com ns_param s2 baz.com } # # Define a server-specific certificate to enable SNI. # Furthermore, activate for server "s1" also OCSP stapling. # ns_section ns/server/s1/module/https { ns_param certificate /usr/local/ns/modules/https/foo.com.pem ns_param OCSPstapling on }
OCSP (abbreviation of Online Certificate Status Protocol, RFC 2560 and RFC 6066) is a protocol that checks the validity status of a certificate. The term "OCSP stapling" refers to the use of digitally-signed time-stamped OCSP responses in the web server for performance reasons. In the example above, OCSP stapling is defined for server "s1".
The virtual servers defined in the last sections have the disadvantage that these require to know the server upfront, to include the server definition in the configuration file. Therefore, adding another virtual server requires a restart which is quick, but still, will interrupt services. In case, the virtual hosts are very similar, it is possible to define a single virtual server hosting multiple domains, serving e.g. from different page directories.
The following example defines server "s1" serving only for HTTP connections over port 8000. The new part is the registration of a callback via ns_serverrootproc, which determines the root directory for serving pages based on the "host" header field. The definition assumes a directory structure like the following, where "foo.com" has, e.g., a different index.html file.
/var/www/ ├── default │ └── index.html ├── foo.com │ └── index.html ...
When the content of the host header field is found in the filesystem under /var/www, it is used as the pageroot for this request.
# # Define server "s1" # - listening on all IP addresses, # - port 8000, # - serve pages from /var/www/ # ns_section ns/servers { ns_param s1 "Sample Server" } ns_section ns/server/s1/modules { ns_param http nssock.so } ns_section ns/server/s1/module/http { ns_param address 0.0.0.0 ns_param port 8000 } ns_section ns/server/s1/fastpath { ns_param pagedir "" ns_param serverdir /var/www } ns_section ns/server/s1/tcl { ns_param initcmds { # # Register callback for accessing the root directory: # ns_serverrootproc { set rootDir [ns_server serverdir] set dir default if {[ns_conn isconnected]} { set host [ns_conn host ""] if {$host ne ""} { # # A "host" header field was provided in the request, # build the name of the root folder. # if {[::file isdirectory $rootDir/$host]} { # # The folder exists in the filesystem, accept it. # set dir $host } else { ns_log debug "... $rootDir/$host does not exist, fallback to $dir" } } } set rootDir $rootDir/$dir ns_log debug "... setting rootdir '$rootDir'" return $rootDir } } }
With this approach additional domains can be added at runtime of NaviServer by adding new directories for them.
While the previous section works for mass virtual hosting with plain HTTP, the usage of HTTPS requires certificates, where multi-domain SAN certificates can't be used, since mass hosting is supposed to work without server restart. One cannot anticipate the domain names, which are added.
Starting with NaviServer 5, it is possible to specify a directory containing individual certificates for virtual hosts with the parameter vhostcertificates in the nsssl module definition. When NaviServer receives a request with an SNI hostname, it will search in this directory at runtime for a certificate file with the name based on the SNI hostname, where ".pem" was added
set serverroot /var/www set address 0.0.0.0 set httpsport 443 ns_section ns/server/s1/module/https { ns_param address $address ns_param port $httpsport ns_param certificate $serverroot/default.pem ns_param vhostcertificates $serverroot }
Note that in this setup only a single server configuration is needed (in the example above, the server "s1"). Together with the definition of the ns_serverrootproc, it will provide mass virtual hosting over HTTPS. Below is a minimal setup showing the directory structure for serving dotlrn.net via this mechanism. Note the certificates dotlrn.net.pem, example.com.pem, and default.pem at the top level directory and their respective subdirectories from where the websites are served. Any request with a different host than example.com or dotlrn.net will be served from the default directory with the default.pem certificate.
/var/www/ ├── default │ └── index.html ├── example.com.pem ├── example.com │ └── index.html ├── dotlrn.net.pem ├── dotlrn.net │ ├── index.html │ └── test │ └── file └── default.pem
It is a common requirement to redirect incoming traffic from HTTP to HTTPS. When a user has connected once via HTTPS, it is recommended to use HSTS (HTTP Strict-Transport-Security) to cause the browser to stay on the secure connection. This is recommended to avoid certain man-in-the-middle attacks.
One simple approach to do this is to define a server configuration listening on the HTTP port that redirects requests to the HTTPS port. In the following example, this server configuration is called "redirector". It accepts requests for HTTP on port 80, and redirects all GET requests to the same server to HTTPS (port 443). This approach can be used for all described virtual host configurations.
# # Simple redirecting server # ns_section ns/servers { ns_param redirector "Redirecting Server" } ns_section ns/server/redirector/modules { ns_param http nssock.so } ns_section ns/server/redirector/module/http { ns_param address 0.0.0.0 ns_param port 80 } ns_section ns/server/redirector/tcl { ns_param initcmds { # # Register an HTTP redirect for every GET request. # ns_register_proc GET /* { set host [ns_conn host localhost] ns_returnmoved https://$host:443[ns_conn target] } } }
For certain sites, it is desired that HTTP request to some paths should be excluded from the HTTPS redirection. E.g., Let's Encrypt requires requests over plain HTTP to the directory /.well-known for certificate management requests (see RFC 8615 and https://letsencrypt.org/docs/challenge-types/).
In order to exclude this directory from the general redirection register the /.well-known target via ns_register_fastpath:
ns_section ns/server/redirector/tcl { ns_param initcmds { # # Register an HTTP redirect for every GET request. # ns_register_proc GET /* { set host [ns_conn host localhost] ns_returnmoved https://$host:443[ns_conn target] } # # Let requests for /.well-known get through. Remember to # define page root, logging etc. for this server configuration. # ns_register_fastpath GET /.well-known } }
For security reasons, it is a common requirement to provide additional header fields to the responses of a server. For scripted pages, such additional header fields can be added depending on the needs of these pages (e.g., by providing page specific CSP policies). However, there are as well requirements for adding response header fields for all requests. The following response header fields are often recommended:
X-Frame-Options "SAMEORIGIN" X-Content-Type-Options "nosniff" X-XSS-Protection "1; mode=block" Referrer-Policy "strict-origin"
or for HTTPS requests
Strict-Transport-Security "max-age=31536000; includeSubDomains"
or, e.g., CSP rules on static pages to address the security threads of SVG files.
In the NaviServer configuration file, one can specify extra response header files for drivers and/or for servers, as the following example shows. The specified header files are merged together. In case, the same header field is provided at the level of the driver and the server, the more specific (i.e., the server-specific) value is used.
ns_section ns/servers { ns_param s1 "Server s1" } ns_section ns/modules { ns_param http nssock.so ns_param https nsssl.so } ns_section ns/module/http { ns_param address localhost ns_param port 8081 # # Driver-specific extra response header fields for HTTP requests # ns_param extraheaders { X-driver-extra http X-extra driver-http } } ns_section ns/module/https { ns_param address localhost ns_param port 8441 ns_param certificate /var/www/default.pem # # Driver-specific extra response header fields for HTTPS requests # ns_param extraheaders { X-driver-extra https X-extra driver-https } } ns_section ns/server/s1/fastpath {ns_param pagedir /var/www/s1} ns_section ns/server/s1/adp {ns_param map "/*.adp"} # # Server-specific extra response header fields for all requests to # this server. # ns_section ns/server/s1 { ns_param extraheaders { X-server-extra s1 X-extra server-s1 } }
When the configuration above is used, all requests via HTTPS will be served with the following extra response header fields.
X-driver-extra https X-server-extra s1 X-extra server-s1
More to come here...
In certain situations, NaviServer returns dynamic content, which is not user generated. Such situations are, e.g., error messages, directorly listings, or other situations, where a developer choses to use ns_returnnotice.
The output can be configured via the ADP template SERVERHOME/conf/returnnotice.adp which receives the variables title, notice, and noticedetail. For custom styling, other templates can be configured via the configuration parameter noticeadp in the server section in the configuration file (here, for the server configuration s1).
ns_section ns/server/s1 { # ... ns_param noticeadp returnnotice.adp ;# ADP file for ns_returnnotice #ns_param noticedetail false ;# return detail information in server reply # ... }
When the configured value for name noticeadp is not absolute, it is assumed to be located in the directory SERVERHOME/conf/. When the parameter is set to an empty string, the old-style hard-coded template is used.
When the custom template leads to an error, a hardcoded basic template is used.
The configuration parameter noticedetail can be used to control, whether the detailed server version information should be included in these server replies (revealing this kind of information is often not wanted for security reasons).
In addition to the basic templating configuration, on can provide tailored error messages depending on the HTTP status codes.
#--------------------------------------------------------------------- # Special HTTP pages #--------------------------------------------------------------------- ns_section ns/server/s1/redirects { ns_param 404 /shared/404 ns_param 403 /shared/403 ns_param 503 /shared/503 ns_param 500 /shared/500 }