How-to

Apache directives in ScalWS

This page maps the Apache httpd directives people use most to their ScalWS equivalent and says which ones scalwsctl migrate apache converts for you.

For the migration itself, see Migrating from Apache.

How to read the table

The column “Converted by scalwsctl migrate” uses four values:

  • yes: the migration turns the directive into a site file key.
  • ignored (not needed): the migration skips it without a note, because ScalWS does this by itself or has no setting for it.
  • reported as TODO: the directive is not converted; the migration writes a # TODO: line with the source file and line into the site file.
  • no: the migration does not read the directive at all, and there is no note.

What the migration reads:

  • It follows Include and IncludeOptional (relative to ServerRoot, a directory includes every file in it) and reads every <VirtualHost> block. Directives outside a <VirtualHost> (apache2.conf, mods-enabled/*.conf, conf-enabled/*.conf) are not read, so a directive that is only set there gets no note.
  • <IfModule>, <IfDefine> and <IfVersion> are transparent: their content counts as part of the virtual host.
  • Inside other sections (<Directory>, <Location>, <Files>, <FilesMatch>, …) a directive that the migration does not handle is dropped without a note. The directives it handles anywhere are listed in the rows below (for example Header, SetHandler, AllowOverride, access rules (Require, Order, Allow, Deny), RewriteRule).

Links in the “ScalWS equivalent” column go to the configuration reference. Site keys are shown as they appear in a site file; the same keys exist under tenants[].applications[] in the full configuration form.

Many per-directory settings have a direct equivalent in ScalWS’s .htaccess support, enabled per site with htaccess: true. It covers mod_rewrite, Redirect/RedirectMatch, Require/Order/Allow/Deny, <Files>/<FilesMatch>, ErrorDocument, DirectoryIndex, Header and Expires*.

Virtual hosts, names and files

DirectiveScalWS equivalentConverted by scalwsctl migrateNotes
Listenserver.listenersnoOutside <VirtualHost>. Listeners are server-wide, not per site.
<VirtualHost>one site fileyesEach block becomes a virtual host. *:443 marks it as HTTPS. An HTTP host that only redirects to HTTPS and the HTTPS host with the same ServerName become one site with https_redirect.
ServerNamedomainyesA :port suffix is removed. A block without a usable name is skipped.
ServerAliasaliases, wwwyeswww.<domain> becomes www: alias; a separate host for www.<domain> that only redirects to the bare domain becomes www: redirect. Names that are not domains are dropped with a note.
DocumentRootrootyes
DirectoryIndexstatic.index, php.indexyesWritten to php.index for a PHP site when a name ends in .php, otherwise to static.index when it differs from index.html. Also supported in .htaccess.
<Directory> with AllowOverridehtaccessyesAny AllowOverride other than None, in any <Directory> of the host, gives htaccess: true. ScalWS then reads .htaccess files from the site’s root down to the requested directory. Other directives inside <Directory> are dropped without a note (see above).
Optionsnoneignored (not needed)ScalWS never lists directories and never follows absolute symlinks.
Aliasroutes with a static runtimereported as TODOA static route does not strip its prefix by itself (route /assets with root /srv/app/public serves /srv/app/public/assets/…). Works when the aliased directory’s name matches the URL prefix; otherwise set the route’s rewrite.prefix to replace the prefix.

Access control

DirectiveScalWS equivalentConverted by scalwsctl migrateNotes
Require, Order, Allow, Deny.htaccess Require, security.deny_pathsreported as TODOEvery access rule except Require all granted (the default) is reported as TODO with its section, in the virtual host and inside <Directory>/<Location>; none is dropped silently. Re-create address restrictions in .htaccess (Require [not] ip …, Require local, Order/Allow/Deny, answered 403) or close paths for everyone with deny_paths (answered 404). Require valid-user, user, group, host, env are not supported and close the directory with 403 in .htaccess.
AuthType, AuthName, AuthUserFileroutes[].basic_authreported as TODOA route’s basic_auth checks an htpasswd file (users_file, bcrypt, SHA-256/512 crypt or Apache MD5 entries) with a realm. In .htaccess, Auth* directives are not supported and close the directory with 403 rather than publish it.
.htaccess (mod_rewrite, mod_alias, mod_headers, mod_expires, access)htaccessyesConverted from AllowOverride. Supported: RewriteEngine, RewriteBase, RewriteCond, RewriteRule (flags L, END, R=code, NC, QSA, QSD, NE, F, G, S=n, C), Redirect, RedirectPermanent, RedirectTemp, RedirectMatch, Require, Order/Allow/Deny, <Files>, <FilesMatch>, <IfModule>, ErrorDocument, DirectoryIndex, Header set|append|add|unset, ExpiresActive, ExpiresByType, ExpiresDefault. Refused and logged: [P], RewriteMap, SetHandler, AddHandler, php_value, php_flag, php_admin_*, regular expressions with look-around or back-references inside the pattern. Files up to 64 KiB and 1000 directives; edits apply within 2 seconds.
mod_security (SecRuleEngine, SecRule, …)security.deny_paths, security.rate_limitsreported as TODOScalWS has no rule-based web application firewall. Closest: deny_paths, per-path rate_limits, the deny rules of an application profile, and the built-in protections (strict request parsing, path normalisation, dotfiles never served).

Redirects and rewrites

DirectiveScalWS equivalentConverted by scalwsctl migrateNotes
Redirect, RedirectPermanentredirect, https_redirectyesOnly a redirect of / is converted: to https:// on the same host it becomes https_redirect, to another origin a redirect runtime that keeps the path (status permanent/301, temp/302, seeother/303; default 302, RedirectPermanent 301). Redirects of other paths are reported; they work in .htaccess.
RedirectMatch, RedirectTemp.htaccess RedirectMatch / RedirectTempreported as TODOReported when set directly in the virtual host; inside a section they are dropped without a note.
RewriteEngine.htaccess RewriteEngineignored (not needed)
RewriteCond, RewriteRulehttps_redirect, .htaccess RewriteRuleyesOnly the HTTPS redirect is converted: a RewriteCond on %{HTTPS}, %{SERVER_NAME} or %{HTTP_HOST} followed by a RewriteRule to https:// on the same host (certbot’s pattern) becomes https_redirect: true. Every other RewriteRule is reported as TODO, with the hint to move it into .htaccess.
ErrorDocument.htaccess ErrorDocumentreported as TODOWith htaccess: true: a local document is served with the original status, a URL redirects (302), text is sent as is.

Proxying and application runtimes

DirectiveScalWS equivalentConverted by scalwsctl migrateNotes
ProxyPassproxy.upstream, routesyesOnly directly in the virtual host (inside <Location> it is dropped without a note). ProxyPass / URL sets the site’s proxy; ProxyPass /path URL becomes a route; ProxyPass /path ! is skipped. The target becomes its origin (http://host:port); a target path, or a / target under another prefix (ProxyPass /api http://b/), is reported as TODO, because Apache maps the prefix and a ScalWS route sends the full request path unless it sets rewrite.prefix. balancer:// groups are not converted: define a pool in the main configuration’s upstreams and use it from proxy.backends (see load balancing).
ProxyPassReversenonereported as TODOScalWS does not rewrite Location headers from the upstream. Send the right Host with preserve_host so the application builds correct URLs.
ProxyPreserveHostproxy.preserve_hostyesOn gives preserve_host: true for the site’s proxy and every proxy route; without it the migration sets false (ScalWS’s own default is true).
SetHandler "proxy:unix:…|fcgi://localhost" / proxy:fcgi://host:portphp.fastcgiyesThe PHP-FPM socket becomes unix:/run/php/…sock, a TCP address tcp:host:port. Usually inside <FilesMatch \.php$>, which is fine. ProxyPassMatch to fcgi:// with php in the pattern is converted too. Other SetHandler values are reported. ScalWS can also run PHP-FPM itself with php.fpm.
mod_php (LoadModule php_module, SetHandler application/x-httpd-php, php_value)php.fpm, php.fpm.ininomod_php is configured outside the virtual hosts, so a mod_php site migrates as a static site: add php to it. ScalWS runs PHP only through FastCGI, as an existing pool (php.fastcgi) or a managed PHP-FPM (php.fpm); php_value settings go to php.fpm.ini. SetHandler application/x-httpd-php in the virtual host is reported as TODO, and so is php_value set directly in it.
ScriptAlias (CGI)nonereported as TODOCGI is not supported. Closest: run the program as an HTTP service and use a proxy route.
WSGIScriptAlias, WSGIDaemonProcesspython with python.mode wsgi and python.appreported as TODOScalWS starts the application server (gunicorn, uvicorn or hypercorn) from python.virtualenv and supervises it.
Passenger (PassengerEnabled, PassengerAppRoot, …)node, python, proxyreported as TODONode.js and Python applications run as managed runtimes. There is no Ruby runtime: run the Ruby application server yourself and proxy to it.

TLS

DirectiveScalWS equivalentConverted by scalwsctl migrateNotes
SSLEngine ontlsyesMarks the host as HTTPS. With server.acme.enabled the site gets tls: auto (a certificate from Let’s Encrypt); otherwise tls: manual and a TODO to add the certificate to the HTTPS listener.
SSLCertificateFiletls, server.listeners[].tls.certificatesyesMarks the host as HTTPS like SSLEngine on. The file is named in the TODO when ACME is off; it is not copied.
SSLCertificateKeyFile, SSLCertificateChainFileserver.listeners[].tls.certificates[].keyignored (not needed)For tls: manual, the certificate file holds the chain (end-entity first) and key the private key.
SSLProtocol, SSLCipherSuite, SSLHonorCipherOrder, SSLCompression, SSLSessionTickets, SSLUseStapling, SSLOpenSSLConfCmd, Protocolsnoneignored (not needed)No settings: TLS 1.2 and 1.3 with forward-secret AEAD suites, no OCSP stapling. HTTP/2 is always on (ALPN h2); HTTP/3 is server.listeners[].http3.

Headers, caching and compression

DirectiveScalWS equivalentConverted by scalwsctl migrateNotes
Header.htaccess Headerreported as TODOReported also inside sections. No site-level key for response headers; a route can set them with response_headers. With htaccess: true, Header [always] set|append|add|unset in .htaccess (no env=/expr= conditions; hop-by-hop and framing headers cannot be changed). ScalWS adds no Strict-Transport-Security by itself.
ExpiresActive, ExpiresByType.htaccess ExpiresActive, ExpiresByType, ExpiresDefaultreported as TODOReported also inside sections.
mod_cache (CacheEnable, CacheDefaultExpire, …)cache, server.cachereported as TODOReported when set in the virtual host. ScalWS caches per site with cache (bypass paths, cookies and query parameters, default_ttl, stale_while_revalidate) in the shared memory and optional disk store of server.cache.
mod_deflate (AddOutputFilterByType DEFLATE, SetOutputFilter)static.precompressednoUsually set in mods-enabled/deflate.conf, which is not read (in a virtual host it is reported). ScalWS does not compress responses on the fly; with precompressed: true the static runtime serves an existing <file>.br or <file>.gz. In .htaccess, AddOutputFilterByType is ignored.

Limits and timeouts

DirectiveScalWS equivalentConverted by scalwsctl migrateNotes
LimitRequestBodyserver.limits.max_request_body_bytesreported as TODOReported when set in the virtual host; inside <Directory> it is dropped without a note. Server-wide (default 10 MiB); a route can override it with max_body. A larger Content-Length gets 413.
Timeoutserver.limits.header_read_timeout, server.limits.body_read_timeout, server.limits.write_timeout, proxy.response_timeoutnoUsually global. ScalWS splits it: request head (15 s), gap between body chunks (30 s), stalled writes (60 s), and per runtime the time to the upstream’s response headers. In a virtual host it is reported.
KeepAlive, KeepAliveTimeoutserver.limits.idle_timeoutnoUsually global. There is no setting to turn keep-alive off; idle connections are closed after idle_timeout (default 60 s). In a virtual host they are reported.

Logging and client addresses

DirectiveScalWS equivalentConverted by scalwsctl migrateNotes
CustomLogserver.logging.access_log, server.logging.formatignored (not needed)One line per request, on by default, json or text, written to the service log (journalctl -u scalwsd; under scalws-controller each instance’s own file, see logs). No per-site log files or custom formats.
ErrorLog, LogLevelserver.logging.levelignored (not needed)Also to the service log.
RemoteIPHeader, RemoteIPTrustedProxyserver.trusted_proxies, server.listeners[].proxy_protocolreported as TODOReported when set in the virtual host (mod_remoteip is usually configured globally). For requests from a trusted peer, the client is the right-most X-Forwarded-For entry that is not itself a trusted proxy. Only X-Forwarded-For and the PROXY protocol are read: other headers (X-Real-IP, a CDN’s own header) are not supported.