MERECAT.CONF(5)

πŸ“¦
version 2.31+git20260330 Β· 2 versions
, in ubuntu stonking

🌸 NAME 🌸

merecat.conf β€” bestie, this is the configuration file for merecat httpd πŸ’… yes it's merecat.conf(5), the File Formats Manual page, and we're about to unpack it together 🫢


✨ INTRODUCTION ✨

OK so listen πŸ‘‘ β€” when merecat boots up, it goes looking for its config file at /etc/merecat.conf. That's the default location, no cap.

This whole page is here to document all the settings you can put in there, which is what unlocks the advanced setup vibes πŸ”₯ For the simpler use-cases though? You might not even need a merecat.conf at all, because the server runs perfectly fine with just command line parameters ✨ like, she's low-maintenance, fr fr.


πŸ’œ DESCRIPTION πŸ’œ

The syntax of the config file is UNIX style: key = value, separated by whitespace. That's it, that's the whole aesthetic πŸŽ€

  • The # character marks the start of a comment, running all the way to the end of the line.
  • The \ character can be used as an escape character.
🚨 IMPORTANT NOTE, bestie, do not skip this: changes to the configuration file require a restart of merecat. Unlike many other UNIX daemons, SIGHUP does not reload the .conf file. She does not do hot reloads. She does not take hints. Restart her. πŸ’€

⭐ Configuration Directives ⭐

Here come all the directives, one by one, in order, no skipping πŸ’–

  • charset = "STRING" β€” the character set to use with text MIME types. Default is "UTF-8". If the default unicode charset is giving you trouble, try "iso-8859-1" instead 🌈
  • check-referer = <true | false> β€” turns on checking for external sites referencing material on your web server. For more on referrers, go see merecat(8). Disabled by default.
  • check-symlinks = <true | false> β€” for increased security, set this to true. Unless you're running chrooted, in which case it's not really necessary. Disabled by default.
  • chroot = <true | false> β€” changes the web server root to WEBDIR, or the current directory if no WEBDIR is given as an argument. Chrooting is a security measure and it means merecat.conf cannot access files outside it, unless files are bind mounted, or similar, into the chroot. Disabled by default. πŸ”’
  • compression-level = -1..9 β€” controls the compression level of the built-in Apache-like mod_deflate. The default value is -1, which gives a reasonable compromize between speed and compression. To disable compression set this to 0, and to get maximum compression, set 9 πŸ”₯

The default setting, -1, means all "text/*" MIME type files, larger than 256 bytes, are compressed before sending to the client. πŸŽ€

  • directory = DIR β€” if no WEBDIR is given on the command line, this option can be used to change the web server document root. Defaults to the current directory. When chrooting, this is the root directory β€” see the data-directory directive for more help πŸ’…
  • data-directory = DIR β€” this setting is only relevant when chrooting. It adjusts the web server document root relative to the directory directive. πŸ¦‹
  • global-passwd = <true | false> β€” set this to true to protect the entire directory tree with a single .htpasswd and/or .htaccess file. When unset, which is the default, merecat.conf looks for a local .htpasswd and .htaccess file, or serves the file without password. πŸ”
  • hostname = HOSTNAME β€” the hostname to bind to when multihoming. For more details on this, see the discussion below.
  • list-dotfiles = <true | false> β€” whether dotfiles should be skipped in directory listings. Disabled by default. πŸ™ˆ
  • local-pattern = "PATTERN" β€” used with check-referer. See merecat(8) for more details.
  • max-age = SEC β€” controls the global max-age setting, in seconds, set in the HTTP/1.1 "Cache-Control: max-age" header, returned with all responses. The default setting is disabled since v2.32, and the user is recommended to use per-resource cache control instead. See the server location directive for details ✨
  • port = PORT β€” the web server Internet port to listen to. Defaults to 80, or 443 when HTTPS is enabled. See the ssl section below for more on configuring an HTTPS server πŸ”’
  • url-pattern = "PATTERN" β€” used with check-referer. See merecat(8) for more details.
  • username = "NAME" β€” set the username to drop privileges to after startup. Defaults to "nobody", which usually is defined on all UNIX systems πŸ‘‘
  • virtual-host = <true | false> β€” enable virtual hosting. Disabled by default. For more information on this, see merecat(8).
  • user-agent-deny = "PATTERN" β€” a wildcard pattern to deny access to illicit hammering bots πŸ’€ When set, a matching user-agent will receive a 403 for all its requests. Use for instance "**SemrushBot**" or "**SemrushBot**|**MJ12Bot**|**DotBot**" to match multiple user-agents.

The default is disabled, i.e. all user-agents are allowed. πŸ•ŠοΈ

  • cgi "PATTERN" { β€” wildcard pattern for CGI programs, for instance "**.cgi" or "**.cgi|/cgi-bin/*". See the dedicated CGI section in merecat(8) for more on this.
    • enabled = <true | false> β€” the CGI module is disabled by default.
    • limit = NUM β€” maximum number of allowed simultaneous CGI programs. Default 1.
    • setenv = { NAME=value, ... } β€” list of environment variables to pass to all CGI programs. Use this to inject custom variables that CGI scripts can read from their environment. Example:
setenv = { "MY_VAR=hello", "APP_ENV=production" }
🫧 Environment variables: Merecat scrubs the process environment before forking CGI children, for security reasons. Environment variables set in the shell or via systemd EnvironmentFile are therefore not visible to CGI programs.

Use the setenv option to pass custom variables directly, or use a wrapper script for more complex needs:

#!/bin/sh
export GIT_PROJECT_ROOT=/srv/repos
export GIT_HTTP_EXPORT_ALL=1
exec /usr/lib/git-core/git-http-backend

Install the wrapper as the CGI entry point instead of the program itself πŸŽ€

  • }
  • php "PATTERN" { β€” wildcard pattern for PHP scripts, for instance "**.php*" or "**.php5*|**.php4*|**.php*". πŸ‘€ Notice the trailing * β€” it is very important, otherwise any HTTP GET request with arguments will fail.
    • enabled = <true | false> β€” the PHP module is disabled by default.
    • cgi-path = "/path/to/php-cgi" β€” default is "/usr/bin/php-cgi".
  • }
  • ssi "PATTERN" { β€” wildcard pattern for triggering SSI, for instance "**.shtml" or "**.shtml|**.stm|**.shtm".
    • enabled = <true | false> β€” the SSI module is disabled by default.
    • cgi-path = "/path/to/ssi" β€” default is "cgi-bin/ssi". See ssi(8) for more information.
    • silent = <true | false> β€” this setting can be used to silence β€œ[an error occurred while processing the directive]”, shown when an error occurrs during SSI processing. Default disabled (false). 🀫
  • }
  • ssl { β€” the whole TLS glow-up lives in here πŸ’…
    • protocol = "PROTOCOL" β€” minimum SSL/TLS protocol level to enable. Can be one of: SSLv3, TLSv1, TLSv1.1, TLSv1.2, TLSv1.3. The default minimum protocol is TLSv1.1. Note, some (Linux) distributions have SSLv3 disabled by default in their OpenSSL packages.
    • ciphers = "CIPHERS" β€” the preferred list of ciphers the server supports. For a list of available ciphers, see the ciphers(1) man page. The default covers both TLSv1.3 (new ciphersuite) and older cipher list:
TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256: \
HIGH:!aNULL:!kRSA:!PSK:!SRP:!MD5:!RC4:!DHE-RSA-CAMELLIA256-SHA:             \
!DHE-RSA-CAMELLIA128-SHA:!ECDHE-RSA-CHACHA20-POLY1305:                      \
!DHE-RSA-CHACHA20-POLY1305:!DHE-RSA-AES256-CCM8:!DHE-RSA-AES256-CCM:        \
!DHE-RSA-AES128-CCM8:!DHE-RSA-AES128-CCM
  • certfile = /path/to/cert.pem β€” public part of the HTTPS certificate, optionally with full certificate chain. E.g., fullchain.pem if you use Let's Encrypt. Only PEM format is supported ✨
  • keyfile = /path/to/key.pem β€” private key of the HTTPS certificate, e.g., privkey.pem if you use Let's Encrypt. Only PEM format is supported.
🚨 Note: this file must be kept private and should not be in the WEBROOT directory. Like, seriously, do not do that, bestie 😭
  • dhfile = /path/th/dhparam.pem β€” optional Diffie-Hellman parameters. Not secret β€” unlike the keyfile, the dhfile can be published online, if necessary. Create one like this:
openssl dhparam -out dhparam.pem 2048
  • }
  • server name { β€” a named server block, iconic πŸ‘‘
    • port = PORT β€” server port to listen to.
    • ssl { ... } β€” same as the global settings, above, only for this server.
    • location "PATTERN" {
      • path = path/to/rewrite β€” if a server location directive is found it has precedence over any redirect or virtual host. It is primarily used to rewrite, or redirect, requests inside the current server context.

E.g., for handling certbot HTTP-01 renewal, use this in the port 80 server context. Any other path will be redirected to HTTPS, using the below redirect directive:

location "/.well-known/acme-challenge/**" {
    path = "letsencrypt/.well-known/acme-challenge/"
}
  • redirect "PATTERN" {
    • code = CODE β€” HTTP redirect code to use, default: 301. Supported codes are: 301, 302, 303, 307.
    • location = "proto://$host:port$request_uri$args" β€” location to return for redirect, e.g. to redirect all request for HTTP to HTTPS for the same (virtual) host:
redirect "/**" {
    code = 301
    location = "https://$host$request_uri$args"
}
}

okay and that's the block, all closed up, no loose braces bestie πŸ’…


  • proxy-pass "PATTERN" β€” the reverse-proxy era begins ✨ inside these braces you get three knobs, and here they are:
    • host = "HOSTNAME" β€” Optional, but lowkey the MVP for multihoming 🏠 only forward requests whose Host: header matches this hostname. Pair it with multihoming (virtual-host = true) to route different virtual hosts to different backends on the same port. Say you only want to proxy stuff for "git.example.com":
proxy-pass "/**" {
    host    = "git.example.com"
    backend = "http://localhost:3000"
}

When you don't set it, the rule just applies no matter what the Host: header says. Chaotic but valid 🫢

  • backend = "http://HOST:PORT[/PATH]" β€” forward matching requests to this HTTP backend server, i.e. act as a reverse proxy. It's giving proxy_pass from nginx, fr fr, and it's the main way to front your local app servers (Node.js, Python, Go, etc.) with Merecat πŸ‘‘

The backend hostname gets resolved at startup, so you never block on a DNS lookup in the middle of serving a request. Ngl that's thoughtful. Only plain HTTP backends are supported β€” no HTTPS πŸš«πŸ”’

proxy-pass "/api/**" {
    backend = "http://localhost:3000"
}

Every forwarded request gets these headers added to it, no cap:

  • X-Forwarded-For β€” client IP address (or the value that's already in X-Forwarded-For, if the client is itself a proxy).
  • X-Real-IP β€” client IP address.
  • X-Forwarded-Proto β€” "http" or "https", depending on whether the client connected over a plain or a TLS connection.

Now the fun part πŸŽ€ if the backend URL has a path component in it, the matched URL prefix gets stripped before forwarding, and the backend path gets swapped in. So with:

proxy-pass "/api/**" {
    backend = "http://localhost:3000/v2"
}

a client asking for "/api/users/42" gets forwarded to the backend as "GET /v2/users/42". Iconic. And if the backend URL has no path (or just "/"), the original request URI gets forwarded unchanged, periodt πŸ’…

  • proxy-redirect = "FROM TO" β€” Optional. This one rewrites the Location: and Refresh: response headers the backend sends back. Both FROM and TO are literal URL prefixes, separated by a space. Any response header value that starts with FROM gets rewritten so that prefix becomes TO ✨ Super useful when a backend returns absolute redirects using its own path prefix but your frontend exposes it under a different prefix.

For example, if the backend at "http://localhost:6419" redirects to "http://localhost:6419/grip/" but the frontend exposes the app under "/grip/", you add:

proxy-pass "/grip/**" {
    backend        = "http://localhost:6419/"
    proxy-redirect = "http://localhost:6419 http://localhost"
}

Merecat then rewrites "Location: http://localhost:6419/grip/" into "Location: http://localhost/grip/". Chef's kiss πŸ˜­πŸ‘Œ

πŸ”₯ Heads up bestie: up to 8 proxy-pass rules may be configured per server. Eight. Count them.
}

}

πŸ”­ SEE ALSO πŸ”­

  • merecat(8) β€” go read the big one, it's the vibe πŸ’œ

πŸ‘‘ AUTHORS πŸ‘‘

  • Jef Poskanzer <jef@mail.acme.com> wrote the famous thttpd, which merecat.conf is based on. A legend, honestly 🌈
  • Joachim Wiberg <troglobit@gmail.com> added the .conf file parser and this very man page. Your fave fr fr 🫢

and that's the whole page, bestie β€” that was merecat 3.00, dated Mar 15, 2026, aka the MERECAT.CONF(5) era. ty for reading, go configure something slay πŸ’–πŸ¦‹βœ¨

πŸ“¦ where this page comes from