Users' guide
Domains, routing and default certificates
This page explains how ScalWS picks the application for a request from its host name, how it picks the TLS certificate for a connection from the SNI name, and how the two relate.
Two separate lookups are involved:
- Routing uses the request’s host (the
Hostheader or the request target’s authority) and thedomainsof all applications. It decides which application answers. - Certificate selection uses the SNI name of the TLS handshake and the
server_namesof the listener’s certificates (plus ACME certificates). It decides which certificate the client sees.
A certificate only has to be valid for the name; it does not choose the application.
Configuring domains
tenants:
- name: shop
applications:
- name: storefront
domains: [shop.example.com, www.shop.example.com]
root: /srv/www/shop
runtime: {type: static}
- name: customers
domains: ["*.customers.example.com"]
runtime: {type: proxy, upstream: "http://127.0.0.1:8080"}Every application needs at least one domain (at least one domain is required). Domains
are global: routing does not depend on the listener, so an application answers on every
listener (plain HTTP and TLS) for its domains. Use
https_redirect to
send plain-HTTP requests to HTTPS.
Normalisation
Configured names are normalised when the configuration is loaded:
- lower-cased, and a trailing dot removed (
Example.COM.isexample.com); - each label is 1 to 63 characters of
a-z,0-9and-, and does not start or end with-; the whole name is at most 253 characters; - internationalised names must be written in punycode (
xn--...); - an IPv4 address such as
127.0.0.1is a valid domain (the packaged configuration serveslocalhostand127.0.0.1); IPv6 addresses are not.
An invalid name fails the check with
invalid domain "...": labels must be 1-63 chars of a-z, 0-9 and '-' (use punycode for IDNs).
Request hosts are normalised the same way before the lookup:
- the port is removed (
example.com:8443isexample.com), so routing does not depend on the port; - lower-cased, a trailing dot removed;
- an IPv6 literal is written in its canonical form (
[0::1]and[::0:1]are both[::1]); - a host with an empty label, a character other than letters, digits,
-and_, a label longer than 63 characters, an invalid port, or more than 255 characters in all is refused with400.
Exact names and wildcards
-
An exact name matches only itself.
-
*.example.comcovers exactly one extra label: it matchesa.example.com, but notexample.comand nota.b.example.com. Listexample.comseparately if it should be served too. -
The suffix of a wildcard must have at least two labels:
*.comfails withwildcard "*.com" must cover a multi-label suffix. The*may only be the whole first label (foo.*.comis invalid). -
**.example.comcovers one or more extra labels: it matchesa.example.comanda.b.example.com, neverexample.comitself. Its suffix needs two labels as well (**.comis invalid) and it cannot be combined withacme: true(it would need DNS-01). -
The lookup order is: the exact name,
*.(one label),**.(the longest matching suffix first, so**.b.example.comwins over**.example.com), then the catch-all"*". -
An exact name wins over a wildcard, whatever the order in the configuration:
domains: ["*.example.com"] # application A # ... domains: [api.example.com] # application B: api.example.com goes here
Duplicate domains
A normalised name may be used by one application only, across all tenants and all site files. A second use fails the check, for example:
tenants[1].applications[0].domains[0]: domain "example.com" is already used by blog/blogExample.com, example.com and example.com. are the same name. *.example.com and
a.example.com are different names and may belong to different applications (the exact
name wins).
Unknown hosts
A request whose host matches no domain gets 404 Not Found. It is counted with tenant and
application _unmatched in the request metrics and the access log. There is no catch-all
application; to answer such requests differently, add the names you expect (for example the
server’s IP address) to an application’s domains.
A request without a host, with more than one Host header, or with a Host that differs
from the authority of the request target gets 400. The full order of checks is in
How a request is processed.
Site files and www
A site file in sites.d/ describes one site with a main
domain, optional
aliases and a www
setting. It is expanded into applications with the same rules as above:
www | Result |
|---|---|
none (the default in the file) | www.<domain> is not served (unless another application lists it). |
alias | www.<domain> is added to the site’s domains. |
redirect | A second application <name>-www serves www.<domain> with a 301 redirect to <domain>, path and query kept. The target is https:// when the site uses ACME or the server has a TLS listener, http:// otherwise. |
scalwsctl site add uses --www redirect by default for a domain with one dot such as
example.com, otherwise none. A www. name that is already another site’s domain or
alias is a duplicate and fails the check.
Certificates and SNI
Each TLS listener has its own certificate table, built from its
certificates
and, with acme: true, the
ACME certificates of the applications.
server:
listeners:
- name: https
address: "0.0.0.0:443"
tls:
certificates:
- cert: /etc/scalws/tls/example.com/fullchain.pem
key: /etc/scalws/tls/example.com/privkey.pem
server_names: [example.com, "*.example.com"]
default: true
- cert: /etc/scalws/tls/example.net/fullchain.pem
key: /etc/scalws/tls/example.net/privkey.pem
server_names: [example.net]For each new TLS connection the certificate is chosen from the SNI name:
- an exact entry in
server_names(or an ACME certificate for that name); - otherwise a wildcard entry, with the same one-label rule as for domains;
- otherwise the listener’s
defaultcertificate; - otherwise the handshake fails. It is counted in
scalws_rejections_totalwithreason="tls_handshake".
A client that sends no SNI (for example a request to an IP address) gets the default
certificate, or a failed handshake when there is none.
Rules checked when the configuration is loaded:
- every name in
server_namesis normalised like a domain and must be covered by the certificate (certificate is not valid for server name "..."); - a name may be assigned to one certificate per listener
(
server name "..." is assigned to more than one certificate); - at most one certificate per listener may be
default(at most one certificate may be marked default); - a TLS listener needs at least one certificate or
acme: true(a TLS listener needs at least one certificate (or acme: true)); - when a configured certificate and an ACME certificate cover the same name, the configured
one wins. ACME certificates are exact names only: with
acme: trueon an application, wildcard domains, IP addresses andlocalhostare refused.
Until an application has its first ACME certificate, handshakes for its names get the
listener’s default certificate, or fail without one. Marking a certificate default
therefore keeps HTTPS answering (with a certificate warning in browsers) while orders are
pending. More in tls.md.
Strict SNI and 421
Browsers may reuse one HTTP/2 connection for several host names when the certificate covers them all (connection coalescing). The request’s host then differs from the SNI name of the connection, and ScalWS routes it by the host as usual.
With strict_sni: true
on a TLS listener, such a request gets 421 Misdirected Request, and the client retries on
a new connection whose SNI matches. The comparison uses the normalised host: for an exact
domain the configured name, otherwise the request host. Requests on connections without SNI
are not affected, and a host that matches no application still gets 404 (the host lookup
comes first). Refusals are counted in scalws_security_blocked_total with
reason="misdirected".
strict_sni on a listener without TLS fails the check with only applies to TLS listeners.
See also
- request-processing.md: where the host lookup and strict SNI sit in the order of checks.
- tls.md: certificates, ACME, TLS versions and HTTP/3.
- ../sites.md: site files.
- applications.md: applications and their domains.