Server configuration
Ferron 2.0.0 and newer can be configured in a KDL-format configuration file (often named ferron.kdl). Below are the descriptions of configuration properties for this server.
Configuration blocks
At the top level of the server configration, the confguration blocks representing specific virtual host are specified. Below are the examples of such configuration blocks:
globals {
// Global configuration that doesn't imply any virtual host
}
* {
// Global configuration
}
*:80 {
// Configuration for port 80
}
example.com {
// Configuration for "example.com" virtual host
}
"192.168.1.1" {
// Configuration for "192.168.1.1" IP virtual host
}
example.com:8080 {
// Configuration for "example.com" virtual host with port 8080
}
"192.168.1.1:8080" {
// Configuration for "192.168.1.1" IP virtual host with port 8080
}
api.example.com {
// Below is the location configuration for paths beginning with "/v1/". If there was "remove_base=#true", the request URL for the location would be rewritten to remove the base URL
location "/v1" remove_base=#false {
// ...
// Below is the error handler configuration for any status code.
error_config {
// ...
}
}
// The location and conditionals' configuration order is automatically determined based on the location and conditionals' depth
location "/" {
// ...
}
// Below is the error handler configuration for 404 Not Found status code. If "404" wasn't included, it would be for all errors.
error_config 404 {
// ...
}
}
example.com,example.org {
// Configuration for example.com and example.org
// The virtual host identifiers (like example.com or "192.168.1.1") are comma-separated, but adding spaces will not be interpreted,
// For example "example.com, example.org" will not work for "example.org", but "example.com,example.org" will work.
}
with-conditions.example.com {
condition "SOME_CONDITION" {
// Here are defined subconditions in the condition. The condition will pass if all subconditions will also pass
}
if "SOME_CONDITION" {
// Conditional configuration
// Conditions can be nested
}
if_not "SOME_CONDITION" {
// Configuration, in case of condition not being met
}
}
snippet "EXAMPLE" {
// Example snippet configuration
}
with-snippet.example.com {
// Import from snippet
use "EXAMPLE"
}
with-snippet.example.org {
// Snippets can be reusable
use "EXAMPLE"
}
inheritance.example.com {
// The "proxy" directive is used as an example for demonstrating inheritance.
proxy "http://10.0.0.2:3000"
proxy "http://10.0.0.3:3000"
// Here, these directives take effect:
// proxy "http://10.0.0.2:3000"
// proxy "http://10.0.0.3:3000"
location "/somelocation" {
// Here, `some_directive` directives are inherited from the parent block.
// These directives take effect:
// proxy "http://10.0.0.2:3000"
// proxy "http://10.0.0.3:3000"
}
location "/anotherlocation" {
// The directives from the parent block are not inherited if there are other directives with the same name in the block.
// Here, these directives take effect:
// proxy "http://10.0.0.4:3000"
proxy "http://10.0.0.4:3000"
}
}Also, it’s possible to include other configuration files using an include <included_configuration_path: string> directive, like this:
include "/etc/ferron.d/**/*.kdl"Directive categories overview
This configuration reference organizes directives by both scope (where they can be used) and functional categories (what they do). This makes it easier to find the directives you need based on your specific requirements.
Scopes
- Global-only - can only be used in the global configuration scope
- Global and virtual host - can be used in both global and virtual host scopes
- General directives - can be used in various scopes including virtual hosts and location blocks
Functional categories
- TLS/SSL & security - certificate management, encryption settings, and security policies
- HTTP protocol & performance - protocol settings, timeouts, and performance tuning
- Networking & system - network configuration and system-level settings
- Caching - HTTP caching configuration and cache management
- Load balancing - health checks and load balancer settings
- Static file serving - file serving, compression, and directory listings
- URL processing & routing - URL rewriting, redirects, and routing rules
- Headers & response customization - custom headers and response modification
- Security & access control - authentication, authorization, and access restrictions
- Reverse proxy & load balancing - proxy configuration and backend management
- Forward proxy - forward proxy functionality
- Authentication forwarding - external authentication integration
- CGI & application servers - CGI, FastCGI, SCGI, and other gateway interfaces configuration
- Content processing - response body modification and filtering
- Rate limiting - request rate limiting and throttling
- Logging - access and error logging configuration
Global-only directives
TLS/SSL & security
tls_cipher_suite <tls_cipher_suite: string> [<tls_cipher_suite_2: string> ...]- This directive specifies the supported TLS cipher suites. If using the HTTP/3 protocol (which is experimental in Ferron), the
TLS_AES_128_GCM_SHA256cipher suite needs to be enabled (it’s enabled by default), otherwise the HTTP/3 server wouldn’t start at all. This directive can be specified multiple times. Default: default TLS cipher suite for Rustls
- This directive specifies the supported TLS cipher suites. If using the HTTP/3 protocol (which is experimental in Ferron), the
tls_ecdh_curves <ecdh_curve: string> [<ecdh_curve: string> ...]- This directive specifies the supported TLS ECDH curves. This directive can be specified multiple times. Default: default ECDH curves for Rustls
tls_client_certificate [tls_client_certificate: bool|string]- This directive specifies whenever the TLS client certificate verification is enabled. If set to
#true, the client certificate will be verified against the system certificate store. If set to a string (supported on Ferron UNRELEASED and newer), the client certificate will be verified against the certificate authority in the specified path. Default:tls_client_certificate #false
- This directive specifies whenever the TLS client certificate verification is enabled. If set to
tls_min_version <tls_min_version: string>- This directive specifies the minimum TLS version (TLSv1.2 or TLSv1.3) that the server will accept. Default:
tls_min_version "TLSv1.2"
- This directive specifies the minimum TLS version (TLSv1.2 or TLSv1.3) that the server will accept. Default:
tls_max_version <tls_max_version: string>- This directive specifies the maximum TLS version (TLSv1.2 or TLSv1.3) that the server will accept. Default:
tls_max_version "TLSv1.3"
- This directive specifies the maximum TLS version (TLSv1.2 or TLSv1.3) that the server will accept. Default:
ocsp_stapling [enable_ocsp_stapling: bool]- This directive specifies whenever OCSP stapling is enabled. Default:
ocsp_stapling #true
- This directive specifies whenever OCSP stapling is enabled. Default:
block <blocked_ip: string> [<blocked_ip: string> ...]- This directive specifies IP addresses to be blocked. This directive can be specified multiple times. Default: none
allow <allowed_ip: string> [<allowed_ip: string> ...]- This directive specifies IP addresses to be allowed. This directive can be specified multiple times. Default: none
auto_tls_on_demand_ask <auto_tls_on_demand_ask_url: string|null>- This directive specifies the URL to be used for asking whenever to the hostname for automatic TLS on demand is allowed. The server will append the
domainquery parameter with the domain name for the certificate to issue as a value to the URL. It’s recommended to configure this option when using automatic TLS on demand to prevent abuse. Default:auto_tls_on_demand_ask #null
- This directive specifies the URL to be used for asking whenever to the hostname for automatic TLS on demand is allowed. The server will append the
auto_tls_on_demand_ask_no_verification [auto_tls_on_demand_ask_no_verification: bool]- This directive specifies whenever the server should not verify the TLS certificate of the automatic TLS on demand asking endpoint. Default:
auto_tls_on_demand_ask_no_verification #false
- This directive specifies whenever the server should not verify the TLS certificate of the automatic TLS on demand asking endpoint. Default:
Configuration example:
* {
tls_cipher_suite "TLS_AES_256_GCM_SHA384" "TLS_AES_128_GCM_SHA256"
tls_ecdh_curves "secp256r1" "secp384r1"
tls_client_certificate #false
ocsp_stapling
block "192.168.1.100" "10.0.0.5"
allow "192.168.1.0/24" "10.0.0.0/8"
auto_tls_on_demand_ask "https://auth.example.com/check"
auto_tls_on_demand_ask_no_verification #false
}HTTP protocol & performance
default_http_port <default_http_port: integer|null>- This directive specifies the default port for HTTP connections. If set as
default_http_port #null, the implicit default HTTP port is disabled. Default:default_http_port 80
- This directive specifies the default port for HTTP connections. If set as
default_https_port <default_https_port: integer|null>- This directive specifies the default port for HTTPS connections. If set as
default_https_port #null, the implicit default HTTPS port is disabled. Default:default_https_port 443
- This directive specifies the default port for HTTPS connections. If set as
protocols <protocol: string> [<protocol: string> ...]- This directive specifies the enabled protocols for the web server. The supported protocols are
"h1"(HTTP/1.x),"h2"(HTTP/2) and"h3"(HTTP/3; experimental). Default:protocols "h1" "h2"
- This directive specifies the enabled protocols for the web server. The supported protocols are
timeout <timeout: integer|null>- This directive specifies the maximum time (in milliseconds) for server to process the request, after which the server resets the connection. If set as
timeout #null, the timeout is disabled. It’s not recommended to disable the timeout, as this might leave the server vulnerable to Slow HTTP attacks. Default:timeout 300000
- This directive specifies the maximum time (in milliseconds) for server to process the request, after which the server resets the connection. If set as
h2_initial_window_size <h2_initial_window_size: integer>- This directive specifies the HTTP/2 initial window size. Default: Hyper defaults
h2_max_frame_size <h2_max_frame_size: integer>- This directive specifies the maximum HTTP/2 frame size. Default: Hyper defaults
h2_max_concurrent_streams <h2_max_concurrent_streams: integer>- This directive specifies the maximum amount of concurrent HTTP/2 streams. Default: Hyper defaults
h2_max_header_list_size <h2_max_header_list_size: integer>- This directive specifies the maximum HTTP/2 frame size. Default: Hyper defaults
h2_enable_connect_protocol [h2_enable_connect_protocol: bool]- This directive specifies whenever the CONNECT protocol in HTTP/2 is enabled. Default: Hyper defaults
protocol_proxy [enable_proxy_protocol: bool]- This directive specifies whenever the PROXY protocol acceptation is enabled. If enabled, the server will expect the PROXY protocol header at the beginning of each connection. Default:
protocol_proxy #false
- This directive specifies whenever the PROXY protocol acceptation is enabled. If enabled, the server will expect the PROXY protocol header at the beginning of each connection. Default:
buffer_request <request_buffer_size: integer|null>- This directive specifies the buffer size in bytes for incoming requests. If set as
buffer_request #null, the request buffer is disabled. The request buffer can serve as an additional protection for underlying backend servers against Slowloris-style attacks. Default:buffer_request #null
- This directive specifies the buffer size in bytes for incoming requests. If set as
buffer_response <response_buffer_size: integer|null>- This directive specifies the buffer size in bytes for outgoing responses. If set as
buffer_response #null, the response buffer is disabled. Default:buffer_response #null
- This directive specifies the buffer size in bytes for outgoing responses. If set as
Configuration example:
* {
default_http_port 80
default_https_port 443
protocols "h1" "h2" "h3"
timeout 300000
h2_initial_window_size 65536
h2_max_frame_size 16384
h2_max_concurrent_streams 100
h2_max_header_list_size 8192
h2_enable_connect_protocol
protocol_proxy #false
buffer_request #null
buffer_response #null
}Caching
cache_max_entries <cache_max_entries: integer|null>(cache module)- This directive specifies the maximum number of entries that can be stored in the HTTP cache. If set as
cache_max_entries #null, the cache can theoretically store an unlimited number of entries. The cache keys for entries depend on the request method, the rewritten request URL, the “Host” header value, and varying request headers. Default:cache_max_entries 1024
- This directive specifies the maximum number of entries that can be stored in the HTTP cache. If set as
Configuration example:
* {
cache_max_entries 2048
}Networking & system
listen_ip <listen_ip: string>- This directive specifies the IP address to listen. Default:
listen_ip "::1"
- This directive specifies the IP address to listen. Default:
io_uring [enable_io_uring: bool]- This directive specifies whenever
io_uringis enabled. This directive has no effect for systems that don’t supportio_uringand for web server builds that use Tokio instead of Monoio. Default:io_uring #true
- This directive specifies whenever
tcp_send_buffer <tcp_send_buffer: integer>- This directive specifies the send buffer size in bytes for TCP listeners. Default: none
tcp_recv_buffer <tcp_recv_buffer: integer>- This directive specifies the receive buffer size in bytes for TCP listeners. Default: none
Configuration example:
* {
listen_ip "0.0.0.0"
io_uring
tcp_send_buffer 65536
tcp_recv_buffer 65536
}Global and virtual host directives
TLS/SSL & security
tls <certificate_path: string> <private_key_path: string>- This directive specifies the path to the TLS certificate and private key. Default: none
auto_tls [enable_automatic_tls: bool]- This directive specifies whenever automatic TLS is enabled. Default:
auto_tls #truewhen port isn’t explicitly specified and if the hostname doesn’t look like a local address (127.0.0.1,::1,localhost), otherwiseauto_tls #false
- This directive specifies whenever automatic TLS is enabled. Default:
auto_tls_contact <auto_tls_contact: string|null>- This directive specifies the email address used to register an ACME account for automatic TLS. Default:
auto_tls_contact #null
- This directive specifies the email address used to register an ACME account for automatic TLS. Default:
auto_tls_cache <auto_tls_cache: string|null>- This directive specifies the directory to store cached ACME data, such as cached account data and certifies. Default: OS-specific directory, for example on GNU/Linux it can be
/home/user/.local/share/ferron-acmefor the “user” user, on macOS it can be/Users/user/Library/Application Support/ferron-acmefor the “user” user, on Windows it can beC:\Users\user\AppData\Local\ferron-acmefor the “user” user. On Docker, it would be/var/lib/ferron-acme.
- This directive specifies the directory to store cached ACME data, such as cached account data and certifies. Default: OS-specific directory, for example on GNU/Linux it can be
auto_tls_letsencrypt_production [enable_auto_tls_letsencrypt_production: bool]- This directive specifies whenever the production Let’s Encrypt ACME endpoint is used. If set as
auto_tls_letsencrypt_production #false, the staging Let’s Encrypt ACME endpoint is used. Default:auto_tls_letsencrypt_production #true
- This directive specifies whenever the production Let’s Encrypt ACME endpoint is used. If set as
auto_tls_challenge <acme_challenge_type: string> [provider=<acme_challenge_provider: string>] [...]- This directive specifies the used ACME challenge type. The supported types are
"http-01"(HTTP-01 ACME challenge),"tls-alpn-01"(TLS-ALPN-01 ACME challenge) and"dns-01"(DNS-01 ACME challenge). Theproviderprop defines the DNS provider to use for DNS-01 challenges. Additional props can be passed as parameters for the DNS provider, see automatic TLS documentation. Default:auto_tls_challenge "tls-alpn-01"
- This directive specifies the used ACME challenge type. The supported types are
auto_tls_directory <auto_tls_directory: string>- This directive specifies the ACME directory URL from which the certificates are obtained. Overrides
auto_tls_letsencrypt_productiondirective. Default: none
- This directive specifies the ACME directory URL from which the certificates are obtained. Overrides
auto_tls_no_verification [auto_tls_no_verification: bool]- This directive specifies whenever to disable the certificate verification of the ACME server. Default:
auto_tls_no_verification #false
- This directive specifies whenever to disable the certificate verification of the ACME server. Default:
auto_tls_profile <auto_tls_profile: string|null>- This directive specifies the ACME profile to use for the certificates. Default:
auto_tls_profile #null
- This directive specifies the ACME profile to use for the certificates. Default:
auto_tls_on_demand <auto_tls_on_demand: bool>- This directive specifies whenever to enable the automatic TLS on demand. The functionality obtains TLS certificates automatically when a website is accessed for the first time. It’s recommended to use either HTTP-01 or TLS-ALPN-01 ACME challenges, as DNS-01 ACME challenges might be slower due to DNS propagation delays. It’s also recommended to configure the
auto_tls_on_demand_askdirective alongside this directive. Default:auto_tls_on_demand #false
- This directive specifies whenever to enable the automatic TLS on demand. The functionality obtains TLS certificates automatically when a website is accessed for the first time. It’s recommended to use either HTTP-01 or TLS-ALPN-01 ACME challenges, as DNS-01 ACME challenges might be slower due to DNS propagation delays. It’s also recommended to configure the
auto_tls_eab (<auto_tls_eab_key_id: string> <auto_tls_eab_key_hmac: string>)|<auto_tls_eab_disabled: null>- This directive specifies the EAB key ID and HMAC for the ACME External Account Binding. The HMAC key value is encoded in a URL-safe Base64 encoding. If set as
auto_tls_eab_disabled #null, the EAB is disabled. Default:auto_tls_eab_disabled #null
- This directive specifies the EAB key ID and HMAC for the ACME External Account Binding. The HMAC key value is encoded in a URL-safe Base64 encoding. If set as
Configuration example:
example.com {
auto_tls
auto_tls_contact "admin@example.com"
auto_tls_cache "/var/cache/ferron-acme"
auto_tls_letsencrypt_production
auto_tls_challenge "tls-alpn-01"
auto_tls_profile "default"
auto_tls_on_demand #false
auto_tls_eab #null
}
manual-tls.example.com {
tls "/etc/ssl/certs/example.com.crt" "/etc/ssl/private/example.com.key"
}Logging
log <log_file_path: string>- This directive specifies the path to the access log file, which contains the HTTP response logs in Combined Log Format. Default: none
error_log <error_log_file_path: string>- This directive specifies the path to the error log file. Default: none
Configuration example:
example.com {
log "/var/log/ferron/example.com.access.log"
error_log "/var/log/ferron/example.com.error.log"
}Directives
Headers & response customization
header <header_name: string> <header_value: string>- This directive specifies a header to be added to HTTP responses. The header values supports placeholders like
{path}which will be replaced with the request path. This directive can be specified multiple times. Default: none
- This directive specifies a header to be added to HTTP responses. The header values supports placeholders like
server_administrator_email <server_administrator_email: string>- This directive specifies the server administrator’s email address to be used in the default 500 Internal Server Error page. Default: none
error_page <status_code: integer> <path: string>- This directive specifies a custom error page to be served by the web server. Default: none
header_remove <header_name: string>- This directive specifies a header to be removed from HTTP responses. This directive can be specified multiple times. Default: none
header_replace <header_name: string> <header_value: string>- This directive specifies a header to be added to HTTP responses, potentially replacing existing headers. The header values supports placeholders like
{path}which will be replaced with the request path. This directive can be specified multiple times. Default: none
- This directive specifies a header to be added to HTTP responses, potentially replacing existing headers. The header values supports placeholders like
Configuration example:
example.com {
header "X-Frame-Options" "DENY"
header "X-Content-Type-Options" "nosniff"
header "X-XSS-Protection" "1; mode=block"
header "Strict-Transport-Security" "max-age=31536000; includeSubDomains"
header "X-Custom-Header" "Custom value with {path} placeholder"
header_remove "X-Header-To-Remove"
header_replace "X-Powered-By" "Ferron"
server_administrator_email "admin@example.com"
error_page 404 "/var/www/errors/404.html"
error_page 500 "/var/www/errors/500.html"
}Security & access control
trust_x_forwarded_for [trust_x_forwarded_for: bool]- This directive specifies whenever to trust the value of the
X-Forwarded-Forheader. It’s recommended to configure this directive if behind a reverse proxy. Default:trust_x_forwarded_for #false
- This directive specifies whenever to trust the value of the
status <status_code: integer> [url=<url: string>|regex=<regex: string>] [location=<location: string>] [realm=<realm: string>] [brute_protection=<enable_brute_protection: bool>] [users=<users: string>] [allowed=<allowed: string>] [not_allowed=<not_allowed: string>] [body=<response_body: string>]- This directive specifies the custom status code. This directive can be specified multiple times. The
urlprop specifies the request path for this status code. Theregexprop specifies the regular expression (like^/ferron(?:$|[/#?])) for the custom status code. Thelocationprop specifies the destination for the redirect; it supports placeholders like{path}which will be replaced with the request path. Therealmprop specifies the HTTP basic authentication realm. Thebrute_protectionprop specifies whenever the brute-force protection is enabled. Theusersprop is a comma-separated list of allowed users for HTTP authentication. Theallowedprop is a comma-separated list of IP addresses applicable for the status code. Thenot_allowedprop is a comma-separated list of IP addresses not applicable for the status code. Thebodyprop specifies the response body to be sent. Default: none
- This directive specifies the custom status code. This directive can be specified multiple times. The
user [username: string] [password_hash: string]- This directive specifies an user with a password hash used for the HTTP basic authentication (it can be either Argon2, PBKDF2, or
scryptone). It’s recommended to use theferron-passwdtool to generate the password hash. This directive can be specified multiple times. Default: none
- This directive specifies an user with a password hash used for the HTTP basic authentication (it can be either Argon2, PBKDF2, or
Configuration example:
example.com {
trust_x_forwarded_for
// Basic authentication with custom status codes
status 401 url="/admin" realm="Admin Area" users="admin,moderator"
status 403 url="/restricted" allowed="192.168.1.0/24" body="Access denied"
status 301 url="/old-page" location="/new-page"
// User definitions for authentication (use `ferron-passwd` to generate password hashes)
users "admin" "$2b$10$hashedpassword12345"
users "moderator" "$2b$10$anotherhashedpassword"
}URL processing & routing
allow_double_slashes [allow_double_slashes: bool]- This directive specifies whenever double slashes are allowed in the URL. Default:
allow_double_slashes #false
- This directive specifies whenever double slashes are allowed in the URL. Default:
no_redirect_to_https [no_redirect_to_https: bool]- This directive specifies whenever not to redirect from HTTP URL to HTTPS URL. This directive is always effectively set to
no_redirect_to_httpswhen the server port is explicitly specified in the configuration. Default:no_redirect_to_https #false
- This directive specifies whenever not to redirect from HTTP URL to HTTPS URL. This directive is always effectively set to
wwwredirect [enable_wwwredirect: bool]rewrite <regex: string> <replacement: string> [directory=<directory: bool>] [file=<file: bool>] [last=<last: bool>] [allow_double_slashes=<allow_double_slashes: bool>]- This directive specifies the URL rewriting rule. This directive can be specified multiple times. The first value is a regular expression (like
^/ferron(?:$|[/#?])). Thedirectoryprop specifies whenever the rewrite rule is applied when the path would correspond to directory (if#false, then it’s not applied). Thefileprop specifies whenever the rewrite rule is applied when the path would correspond to file (if#false, then it’s not applied). Thelastprop specifies whenever the rewrite rule is the last rule applied. Theallow_double_slashesprop specifies whenever the rewrite rule allows double slashes in the request URL. Default: none
- This directive specifies the URL rewriting rule. This directive can be specified multiple times. The first value is a regular expression (like
rewrite_log [rewrite_log: bool]- This directive specifies whenever URL rewriting operations are logged into the error log. Default:
rewrite_log #false
- This directive specifies whenever URL rewriting operations are logged into the error log. Default:
no_trailing_redirect [no_trailing_redirect: bool]- This directive specifies whenerver not to redirect the URL without a trailing slash to one with a trailing slash, if it refers to a directory. Default:
no_trailing_redirect #false
- This directive specifies whenerver not to redirect the URL without a trailing slash to one with a trailing slash, if it refers to a directory. Default:
Configuration example:
example.com {
allow_double_slashes #false
no_redirect_to_https #false
wwwredirect #false
// URL rewriting examples
rewrite "^/old-path/(.*)" "/new-path/$1" last=#true
rewrite "^/api/v1/(.*)" "/api/v2/$1" file=#false directory=#false
rewrite "^/blog/([^/]+)/?(?:$|[?#])" "/blog.php?slug=$1" last=#true
rewrite_log
no_trailing_redirect #false
}Static file serving
root <webroot: string|null>- This directive specifies the webroot from which static files are served. If set as
root #null, the static file serving functionality is disabled. Default: none
- This directive specifies the webroot from which static files are served. If set as
etag [enable_etag: bool](static module)- This directive specifies whenever the ETag header is enabled. Default:
etag #true
- This directive specifies whenever the ETag header is enabled. Default:
compressed [enable_compression: bool](static module)- This directive specifies whenever the HTTP compression for static files is enabled. Default:
compressed #true
- This directive specifies whenever the HTTP compression for static files is enabled. Default:
directory_listing [enable_directory_listing: bool](static module)- This directive specifies whenever the directory listings are enabled. Default:
directory_listing #false
- This directive specifies whenever the directory listings are enabled. Default:
precompressed [enable_precompression: bool](static module)- This directive specifies whenever serving the precompressed static files is enabled. The precompressed static files would additionally have
.gzextension for gzip,.deflatefor Deflate,.brfor Brotli, or.zstfor Zstandard. Default:precompressed #false
- This directive specifies whenever serving the precompressed static files is enabled. The precompressed static files would additionally have
Configuration example:
example.com {
root "/var/www/example.com"
etag
compressed
directory_listing #false
// Set "Cache-Control" header for static files
file_cache_control "public, max-age=3600"
}Caching
cache [enable_cache: bool](cache module)- This directive specifies whenever the HTTP cache is enabled. Default:
cache #false
- This directive specifies whenever the HTTP cache is enabled. Default:
cache_max_response_size <cache_max_response_size: integer|null>(cache module)- This directive specifies the maximum size of the response (in bytes) that can be stored in the HTTP cache. If set as
cache_max_response_size #null, the cache can theoretically store responses of any size. Default:cache_max_response_size 2097152
- This directive specifies the maximum size of the response (in bytes) that can be stored in the HTTP cache. If set as
cache_vary <varying_request_header: string> [<varying_request_header: string> ...](cache module)- This directive specifies the request headers that are used to vary the cache entries. This directive can be specified multiple times. Default: none
cache_ignore <ignored_response_header: string> [<ignored_response_header: string> ...](cache module)- This directive specifies the response headers that are ignored when caching the response. This directive can be specified multiple times. Default: none
file_cache_control <cache_control: string|null>(static module)- This directive specifies the Cache-Control header value for static files. If set as
file_cache_control #null, the Cache-Control header is not set. Default:file_cache_control #null
- This directive specifies the Cache-Control header value for static files. If set as
Configuration example:
example.com {
cache
cache_max_response_size 2097152
cache_vary "Accept-Encoding" "Accept-Language"
cache_ignore "Set-Cookie" "Cache-Control"
}Reverse proxy & load balancing
proxy <proxy_to: string|null> [unix=<unix_socket_path: string>](rproxy module)- This directive specifies the URL to which the reverse proxy should forward requests. HTTP (for example
http://localhost:3000/) and HTTPS URLs (for examplehttps://localhost:3000/) are supported. Unix sockets are also supported via theunixprop set to the path to the socket, supported only on Unix and Unix-like systems. This directive can be specified multiple times. Default: none
- This directive specifies the URL to which the reverse proxy should forward requests. HTTP (for example
lb_health_check [enable_lb_health_check: bool](rproxy module)- This directive specifies whenever the load balancer passive health check is enabled. Default:
lb_health_check #false
- This directive specifies whenever the load balancer passive health check is enabled. Default:
lb_health_check_max_fails <max_fails: integer>(rproxy module)- This directive specifies the maximum number of consecutive failures before the load balancer marks a backend as unhealthy. Default:
lb_health_check_max_fails 3
- This directive specifies the maximum number of consecutive failures before the load balancer marks a backend as unhealthy. Default:
proxy_no_verification [proxy_no_verification: bool](rproxy module)- This directive specifies whenever the reverse proxy should not verify the TLS certificate of the backend. Default:
proxy_no_verification #false
- This directive specifies whenever the reverse proxy should not verify the TLS certificate of the backend. Default:
proxy_intercept_errors [proxy_intercept_errors: bool](rproxy module)- This directive specifies whenever the reverse proxy should intercept errors from the backend. Default:
proxy_intercept_errors #false
- This directive specifies whenever the reverse proxy should intercept errors from the backend. Default:
proxy_request_header <header_name: string> <header_value: string>(rproxy module)- This directive specifies a header to be added to HTTP requests sent by the reverse proxy. The header values supports placeholders like
{path}which will be replaced with the request path. This directive can be specified multiple times. Default: none
- This directive specifies a header to be added to HTTP requests sent by the reverse proxy. The header values supports placeholders like
proxy_request_header_remove <header_name: string>(rproxy module)- This directive specifies a header to be removed from HTTP requests sent by the reverse proxy. This directive can be specified multiple times. Default: none
proxy_keepalive [proxy_keepalive: bool](rproxy module)- This directive specifies whenever the reverse proxy should keep the connection to the backend alive. Default:
proxy_keepalive #true
- This directive specifies whenever the reverse proxy should keep the connection to the backend alive. Default:
proxy_request_header_replace <header_name: string> <header_value: string>(rproxy module)- This directive specifies a header to be added to HTTP requests sent by the reverse proxy, potentially replacing existing headers. The header values supports placeholders like
{path}which will be replaced with the request path. This directive can be specified multiple times. Default: none
- This directive specifies a header to be added to HTTP requests sent by the reverse proxy, potentially replacing existing headers. The header values supports placeholders like
proxy_http2 [enable_proxy_http2: bool](rproxy module)- This directive specifies whenever the reverse proxy can use HTTP/2 protocol when connecting to backend servers. Default:
proxy_http2 #false
- This directive specifies whenever the reverse proxy can use HTTP/2 protocol when connecting to backend servers. Default:
lb_retry_connection [enable_lb_retry_connection: bool](rproxy module)- This directive specifies whenever the load balancer should retry connections to another backend server, in case of TCP connection or TLS handshake failure. Default:
lb_retry_connection #true
- This directive specifies whenever the load balancer should retry connections to another backend server, in case of TCP connection or TLS handshake failure. Default:
lb_algorithm <lb_algorithm: string>(rproxy module)- This directive specifies the load balancing algorithm to be used. The supported algorithms are
random(random selection),round-robin(round-robin),least_conn(least connections, “connections” would mean concurrent requests here), andtwo_random(power of two random choices; after two random choices, the backend server with the least concurrent requests is chosen). Default:lb_algorithm "two_random"
- This directive specifies the load balancing algorithm to be used. The supported algorithms are
lb_health_check_window <lb_health_check_window: integer>(rproxy module)- This directive specifies the window size (in milliseconds) for load balancer health checks. Default:
lb_health_check_window 5000
- This directive specifies the window size (in milliseconds) for load balancer health checks. Default:
proxy_keepalive_idle_conns <proxy_keepalive_idle_conns: integer>(rproxy module)- This directive specifies the maximum number of idle connections to backend servers to keep alive. Default:
proxy_keepalive_idle_conns 48
- This directive specifies the maximum number of idle connections to backend servers to keep alive. Default:
Configuration example:
api.example.com {
// Backends for load balancing
// (or you can also use a single backend by specifying only one `proxy` directive)
proxy "http://backend1:8080"
proxy "http://backend2:8080"
proxy "http://backend3:8080"
// Health check configuration
lb_health_check
lb_health_check_max_fails 3
lb_health_check_window 5000
// Proxy settings
proxy_no_verification #false
proxy_intercept_errors #false
proxy_keepalive
proxy_http2 #false
// Proxy headers
proxy_request_header "X-Custom-Header" "CustomValue"
proxy_request_header_remove "X-Internal-Token"
proxy_request_header_replace "X-Real-IP" "{client_ip}"
}Forward proxy
forward_proxy [enable_forward_proxy: bool](fproxy module)- This directive specifies whenever the forward proxy functionality is enabled. Default:
forward_proxy #false
- This directive specifies whenever the forward proxy functionality is enabled. Default:
Configuration example:
* {
forward_proxy
}Authentication forwarding
auth_to <auth_to: string|null>(fauth module)- This directive specifies the URL to which the web server should send requests for forwarded authentication. Default: none
auth_to_no_verification [auth_to_no_verification: bool](fauth module)- This directive specifies whenever the server should not verify the TLS certificate of the backend authentication server. Default:
auth_to_no_verification #false
- This directive specifies whenever the server should not verify the TLS certificate of the backend authentication server. Default:
auth_to_copy <request_header_to_copy: string> [<request_header_to_copy: string> ...](fauth module)- This directive specifies the request headers that will be copied and sent to the forwarded authentication backend server. This directive can be specified multiple times. Default: none
Configuration example:
app.example.com {
// Forward authentication to external service
auth_to "https://auth.example.com/validate"
auth_to_no_verification #false
auth_to_copy "Authorization" "X-User-Token" "X-Session-ID"
}CGI & application servers
cgi [enable_cgi: bool](cgi module)- This directive specifies whenever the CGI handler is enabled. Default:
cgi #false
- This directive specifies whenever the CGI handler is enabled. Default:
cgi_extension <cgi_extension: string|null>(cgi module)- This directive specifies CGI script extensions, which will be handled via the CGI handler outside the
cgi-bindirectory. This directive can be specified multiple times. Default: none
- This directive specifies CGI script extensions, which will be handled via the CGI handler outside the
cgi_interpreter <cgi_extension: string> <cgi_interpreter: string|null> [<cgi_interpreter_argument: string> ...](cgi module)- This directive specifies CGI script interpreters used by the CGI handler. If CGI interpreter is set to
#null, the default interpreter settings will be disabled. This directive can be specified multiple times. Default: specified for.pl,.py,.sh,.ksh,.csh,.rband.phpextensions, and additionally.exe,.batand.vbsextensions for Windows
- This directive specifies CGI script interpreters used by the CGI handler. If CGI interpreter is set to
cgi_environment <environment_variable_name: string> <environment_variable_value: string>(cgi module)- This directive specifies an environment variable passed into CGI applications. Default: none
scgi <scgi_to: string|null>(scgi module)- This directive specifies whenever SCGI is enabled and the base URL to which the SCGI client will send requests. TCP (for example
tcp://localhost:4000/) and Unix socket URLs (only on Unix systems; for exampleunix:///run/scgi.sock) are supported. Default:scgi #null
- This directive specifies whenever SCGI is enabled and the base URL to which the SCGI client will send requests. TCP (for example
scgi_environment <environment_variable_name: string> <environment_variable_value: string>(scgi module)- This directive specifies an environment variable passed into SCGI server. Default: none
fcgi <fcgi_to: string|null> [pass=<fcgi_pass: bool>](fcgi module)- This directive specifies whenever FastCGI is enabled and the base URL to which the FastCGI client will send requests. The
passprop specified whenever to pass the all the requests to the FastCGI request handler. TCP (for exampletcp://localhost:4000/) and Unix socket URLs (only on Unix systems; for exampleunix:///run/scgi.sock) are supported. Default:fcgi #null pass=#true
- This directive specifies whenever FastCGI is enabled and the base URL to which the FastCGI client will send requests. The
fcgi_php <fcgi_php_to: string|null>(fcgi module)- This directive specifies whenever PHP through FastCGI is enabled and the base URL to which the FastCGI client will send requests for “.php” files. TCP (for example
tcp://localhost:4000/) and Unix socket URLs (only on Unix systems; for exampleunix:///run/scgi.sock) are supported. Default:fcgi_php #null
- This directive specifies whenever PHP through FastCGI is enabled and the base URL to which the FastCGI client will send requests for “.php” files. TCP (for example
fcgi_extension <fcgi_extension: string|null>(fcgi module)- This directive specifies file extensions, which will be handled via the FastCGI handle. This directive can be specified multiple times. Default: none
fcgi_environment <environment_variable_name: string> <environment_variable_value: string>(fcgi module)- This directive specifies an environment variable passed into FastCGI server. Default: none
Configuration example:
cgi.example.com {
// CGI configuration
cgi
cgi_extension ".cgi" ".pl" ".py"
cgi_interpreter ".py" "/usr/bin/python3"
cgi_interpreter ".pl" "/usr/bin/perl"
cgi_environment "PATH" "/usr/bin:/bin"
cgi_environment "SCRIPT_ROOT" "/var/www/cgi-bin"
}
scgi.example.com {
// SCGI configuration
scgi "tcp://localhost:4000/"
scgi_environment "SCRIPT_NAME" "/app"
scgi_environment "SERVER_NAME" "example.com"
}
fastcgi.example.com {
// FastCGI configuration
fcgi "tcp://localhost:9000/" pass=#true
fcgi_php "tcp://localhost:9000/"
fcgi_extension ".php" ".php5"
fcgi_environment "SCRIPT_FILENAME" "/var/www/example.com{path}"
fcgi_environment "DOCUMENT_ROOT" "/var/www/example.com"
}Content processing
replace <searched_string: string> <replaced_string: string> [once=<replace_once: bool>](replace module)- This directive specifies the string to be replaced in a response body, and a replacement string. The
onceprop specifies whenever the string will be replaced once, by default this prop is set to#true. Default: none
- This directive specifies the string to be replaced in a response body, and a replacement string. The
replace_last_modified [preserve_last_modified: bool](replace module)- This directive specifies whenever to preserve the “Last-Modified” header in the response. Default:
replace_last_modified #false
- This directive specifies whenever to preserve the “Last-Modified” header in the response. Default:
replace_filter_types <filter_type: string> [<filter_type: string> ...](replace module)- This directive specifies the response MIME type filters. The filter can be either a specific MIME type (like
text/html) or a wildcard (*) specifying that responses with all MIME types are processed for replacement. This directive can be specified multiple times. Default:replace_filter_types "text/html"
- This directive specifies the response MIME type filters. The filter can be either a specific MIME type (like
Configuration example:
example.com {
// Disabling HTTP compression is required for string replacement
compressed #false
// String replacement in response bodies (works with HTTP compression disabled)
replace "old-company-name" "new-company-name" once=#false
replace "http://old-domain.com" "https://new-domain.com" once=#true
replace_last_modified
replace_filter_types "text/html" "text/css" "application/javascript"
}Rate limiting
limit [enable_limit: bool] [rate=<rate: integer|float>] [burst=<rate: integer|float>](limit module)- This directive specifies whenever the rate limiting is enabled. The
rateprop specifies the maximum average amount of requests per second, defaults to 25 requests per second. Theburstprop specifies the maximum peak amount of requests per second, defaults to 4 times the maximum average amount of requests per second. Default:limit #false
- This directive specifies whenever the rate limiting is enabled. The
Configuration example:
example.com {
// Global rate limiting
limit rate=100 burst=200
// Different rate limits for different paths
location "/api" {
limit rate=10 burst=20
}
location "/login" {
limit rate=5 burst=10
}
}Logging
log_date_format <log_date_format: string>- This directive specifies the date format (according to POSIX) for the access log file. Default:
"%d/%b/%Y:%H:%M:%S %z"
- This directive specifies the date format (according to POSIX) for the access log file. Default:
log_format <log_format: string>- This directive specifies the entry format for the access log file. The placeholders can be found in the reference below the section specifying. Default:
"{client_ip} - {auth_user} [{timestamp}] \"{method} {path_and_query} {version}\" {status_code} {content_length} \"{header:Referer}\" \"{header:User-Agent}\""(Combined Log Format)
- This directive specifies the entry format for the access log file. The placeholders can be found in the reference below the section specifying. Default:
Configuration example:
* {
log_date_format "%d/%b/%Y:%H:%M:%S %z"
log_format "{client_ip} - {auth_user} [{timestamp}] \"{method} {path_and_query} {version}\" {status_code} {content_length} \"{header:Referer}\" \"{header:User-Agent}\""
}Subconditions
Ferron 2.0.0 and newer supports conditional configuration based on conditions. This allows you to configure different settings based on the request method, path, or other conditions.
Below is the list of supported subconditions:
is_remote_ip <remote_ip: string> [<remote_ip: string> ...]- This subcondition checks if the request is coming from a specific remote IP address or a list of IP addresses.
is_forwarded_for <remote_ip: string> [<remote_ip: string> ...]- This subcondition checks if the request (with respect for
X-Forwarded-Forheader) is coming from a specific forwarded IP address or a list of IP addresses.
- This subcondition checks if the request (with respect for
is_not_remote_ip <remote_ip: string> [<remote_ip: string> ...]- This subcondition checks if the request is not coming from a specific remote IP address or a list of IP addresses.
is_not_forwarded_for <remote_ip: string> [<remote_ip: string> ...]- This subcondition checks if the request (with respect for
X-Forwarded-Forheader) is not coming from a specific forwarded IP address or a list of IP addresses.
- This subcondition checks if the request (with respect for
is_equal <left_side: string> <right_side: string>- This subcondition checks if the left side is equal to the right side.
is_not_equal <left_side: string> <right_side: string>- This subcondition checks if the left side is not equal to the right side.
is_regex <value: string> <regex: string> [case_insensitive=<case_insensitive: bool>]- This subcondition checks if the value matches the regular expression. The
case_insensitiveprop specifies whether the regex should be case insensitive (#falseby default).
- This subcondition checks if the value matches the regular expression. The
is_not_regex <value: string> <regex: string> [case_insensitive=<case_insensitive: bool>]- This subcondition checks if the value does not match the regular expression. The
case_insensitiveprop specifies whether the regex should be case insensitive (#falseby default).
- This subcondition checks if the value does not match the regular expression. The
is_rego <rego_policy: string>- This subcondition evaluates a Rego policy.
Rego subconditions
Ferron 2.0.0 and newer supports more advanced subconditions with Rego policies embedded in Ferron configuration.
When writing Rego policies for Ferron subconditions, you need to set the package name to ferron (package ferron line). Ferron checks the pass result of the policy.
Inputs for Rego-based subconditions (input) are as follows:
input.method(string) - the HTTP method of the request (GET,POST, etc.)input.uri(string) - the URI of the request (for example,/index.php?page=1)input.headers(array<string, array>) - the headers of the request. The header names are in lower-case. input.socket_data.client_ip(string) - the client’s IP address.input.socket_data.client_port(number) - the client’s port.input.socket_data.server_ip(string) - the server’s IP address.input.socket_data.server_port(number) - the server’s port.input.socket_data.encrypted(boolean) - whether the connection is encrypted.
You can read more about Rego in Open Policy Agent documentation.
Configuration example utilizing Rego subconditions (denying curl requests):
// Replace "example.com" with your domain name.
example.com {
condition "DENY_CURL" {
is_rego """
package ferron
default pass := false
pass := true if {
input.headers["user-agent"][0] == "curl"
}
pass := true if {
startswith(input.headers["user-agent"][0], "curl/")
}
"""
}
if "DENY_CURL" {
status 403
}
// Serve static files
root "/var/www/html"
}Placeholders
Ferron supports the following placeholders for header values, subconditions, reverse proxying, and redirect destinations:
{path}- the request URI with path (for example,/index.html){path_and_query}- the request URI with path and query string (for example,/index.html?param=value){method}- the request method{version}- the HTTP version of the request{header:<header_name>}- the header value of the request URI{scheme}- the scheme of the request URI (httporhttps), applicable only for subconditions, reverse proxying and redirect destinations.{client_ip}- the client IP address, applicable only for subconditions, reverse proxying and redirect destinations.{client_port}- the client port number, applicable only for subconditions, reverse proxying and redirect destinations.{server_ip}- the server IP address, applicable only for subconditions, reverse proxying and redirect destinations.{server_port}- the server port number, applicable only for subconditions, reverse proxying and redirect destinations.
Log placeholders
Ferron 2.0.0 and newer supports the following placeholders for access logs:
{path}- the request URI with path (for example,/index.html){path_and_query}- the request URI with path and query string (for example,/index.html?param=value){method}- the request method{version}- the HTTP version of the request{header:<header_name>}- the header value of the request URI (-, if header is missing){scheme}- the scheme of the request URI (httporhttps).{client_ip}- the client IP address.{client_port}- the client port number.{server_ip}- the server IP address.{server_port}- the server port number.{auth_user}- the username of the authenticated user (-, if not authenticated){timestamp}- the formatted timestamp of the entry{status_code}- the HTTP status code of the response{content_length}- the content length of the response (-, if not available)
Location block example
Below is an example of Ferron configuration involving location blocks:
example.com {
root "/var/www/example.com"
// Static assets with different settings
location "/static" remove_base=#true {
root "/var/www/static"
compressed
file_cache_control "public, max-age=31536000"
}
// API endpoints with proxy
location "/api" {
proxy "http://backend:8080"
proxy_request_header "X-Forwarded-For" "{client_ip}"
limit rate=50 burst=100
}
// Admin area with authentication
location "/admin" {
status 401 realm="Admin Access" users="admin"
root "/var/www/admin"
}
// PHP files
fcgi_php "tcp://localhost:9000/"
}Complete example combining multiple sections
Below is a complete example of Ferron configuration, combining multiple sections:
// Global configuration
* {
// Protocol and performance settings
protocols "h1" "h2"
h2_initial_window_size 65536
h2_max_concurrent_streams 100
timeout 300000
// Network settings
listen_ip "0.0.0.0"
default_http_port 80
default_https_port 443
// Security defaults
tls_cipher_suite "TLS_AES_256_GCM_SHA384" "TLS_AES_128_GCM_SHA256"
ocsp_stapling
block "192.168.1.100"
// Global caching
cache_max_entries 1024
// Load balancing settings
lb_health_check_window 5000
// Logging
log "/var/log/ferron/access.log"
error_log "/var/log/ferron/error.log"
// Static file defaults
compressed
etag
}
// Define reusable snippets
snippet "security_headers" {
// Common security headers
header "X-Frame-Options" "DENY"
header "X-Content-Type-Options" "nosniff"
header "X-XSS-Protection" "1; mode=block"
header "Referrer-Policy" "strict-origin-when-cross-origin"
}
snippet "cors_headers" {
// CORS headers for API endpoints
header "Access-Control-Allow-Origin" "*"
header "Access-Control-Allow-Methods" "GET, POST, PUT, DELETE, OPTIONS"
header "Access-Control-Allow-Headers" "Authorization, Content-Type, X-Requested-With"
header "Access-Control-Max-Age" "86400"
}
snippet "static_caching" {
// Aggressive caching for static assets
file_cache_control "public, max-age=31536000, immutable"
compressed
etag
}
snippet "admin_protection" {
// Admin area protection
status 401 realm="Admin Area" users="admin,superuser"
users "admin" "$2b$10$hashedpassword12345"
users "superuser" "$2b$10$anotherhashpassword67890"
limit rate=10 burst=20
}
snippet "mobile_condition" {
condition "is_mobile" {
is_regex "{header:User-Agent}" "(Mobile|Android|iPhone|iPad)" case_insensitive=#true
}
}
snippet "admin_ip_condition" {
condition "is_admin_ip" {
is_remote_ip "192.168.1.10" "10.0.0.5"
}
}
snippet "api_request_condition" {
condition "is_api_request" {
is_regex "{path}" "^/api/"
}
}
snippet "static_asset_condition" {
condition "is_static_asset" {
is_regex "{path}" "\\.(css|js|png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot|ico)(?:$|[?#])" case_insensitive=#true
}
}
snippet "development_condition" {
condition "is_development" {
is_equal "{header:X-Environment}" "development"
}
}
// Main website with conditional configuration
example.com {
// TLS configuration
tls "/etc/ssl/certs/example.com.crt" "/etc/ssl/private/example.com.key"
auto_tls_contact "admin@example.com"
// Basic settings
root "/var/www/example.com"
server_administrator_email "admin@example.com"
// Use security headers snippet
use "security_headers"
// Import condition snippets
use "mobile_condition"
use "admin_ip_condition"
use "api_request_condition"
use "static_asset_condition"
use "development_condition"
// Additional security header
header "Strict-Transport-Security" "max-age=31536000; includeSubDomains"
// Conditional configuration based on mobile detection
if "is_mobile" {
// Add headers that aren't inherited
use "security_headers"
header "Strict-Transport-Security" "max-age=31536000; includeSubDomains"
// Mobile-specific settings
header "X-Mobile-Detected" "true"
root "/var/www/example.com/mobile"
// Lighter rate limiting for mobile
limit rate=50 burst=100
}
if_not "is_mobile" {
// Add headers that aren't inherited
use "security_headers"
header "Strict-Transport-Security" "max-age=31536000; includeSubDomains"
// Desktop settings
header "X-Mobile-Detected" "false"
limit rate=100 burst=200
}
// Admin IP gets special treatment
if "is_admin_ip" {
// Add headers that aren't inherited
use "security_headers"
header "Strict-Transport-Security" "max-age=31536000; includeSubDomains"
// No rate limiting for admin IPs
limit #false
// Additional debug headers
header "X-Admin-Access" "true"
header "X-Client-IP" "{client_ip}"
// Enhanced logging for admin access
log "/var/log/ferron/admin-access.log"
}
// Development environment conditional settings
if "is_development" {
// Add headers that aren't inherited
use "security_headers"
header "Strict-Transport-Security" "max-age=31536000; includeSubDomains"
// Development-specific headers
header "X-Environment" "development"
header "X-Debug-Mode" "enabled"
// Disable caching in development
header "Cache-Control" "no-cache, no-store, must-revalidate"
header "Pragma" "no-cache"
header "Expires" "0"
}
if_not "is_development" {
// Add headers that aren't inherited
use "security_headers"
header "Strict-Transport-Security" "max-age=31536000; includeSubDomains"
// Production caching
cache
cache_vary "Accept-Encoding" "Accept-Language"
file_cache_control "public, max-age=3600"
}
// URL rewriting
rewrite "^/old-section/(.*)" "/new-section/$1" last=#true
// Error pages
error_page 404 "/var/www/errors/404.html"
error_page 500 "/var/www/errors/500.html"
// Static assets location with conditional caching
location "/assets" remove_base=#true {
root "/var/www/assets"
if "is_static_asset" {
// Add headers that aren't inherited
use "security_headers"
header "Strict-Transport-Security" "max-age=31536000; includeSubDomains"
use "static_caching"
}
// CORS for web fonts
if_not "is_static_asset" {
// Add headers that aren't inherited
use "security_headers"
header "Strict-Transport-Security" "max-age=31536000; includeSubDomains"
header "Access-Control-Allow-Origin" "*"
}
}
// API endpoints
location "/api" {
if "is_api_request" {
// Add headers that aren't inherited
use "security_headers"
header "Strict-Transport-Security" "max-age=31536000; includeSubDomains"
use "cors_headers"
// API-specific rate limiting
limit rate=1000 burst=2000
// Proxy to API backend
proxy "http://api-backend:8080"
proxy_request_header_replace "X-Real-IP" "{client_ip}"
proxy_request_header "X-Forwarded-Proto" "{scheme}"
}
}
// Admin area with conditional access
location "/admin" {
if "is_admin_ip" {
// Add headers that aren't inherited
use "security_headers"
header "Strict-Transport-Security" "max-age=31536000; includeSubDomains"
// Admin IPs get direct access
root "/var/www/admin"
// Special admin headers
header "X-Admin-Direct-Access" "true"
}
if_not "is_admin_ip" {
// Add headers that aren't inherited
use "security_headers"
header "Strict-Transport-Security" "max-age=31536000; includeSubDomains"
// Non-admin IPs require authentication
use "admin_protection"
}
}
// PHP application
fcgi_php "tcp://localhost:9000/"
}
// API subdomain with extensive conditional logic
api.example.com {
// TLS configuration
tls "/etc/ssl/certs/api.example.com.crt" "/etc/ssl/private/api.example.com.key"
// Use CORS headers snippet
use "cors_headers"
// Import reusable condition
use "admin_ip_condition"
// Conditional backend selection based on request path
condition "is_v1_api" {
is_regex "{path}" "^/v1/"
}
condition "is_v2_api" {
is_regex "{path}" "^/v2/"
}
condition "is_auth_request" {
is_regex "{path}" "^/(login|register|refresh|logout)"
}
// Version-specific backend routing
if "is_v1_api" {
// Use CORS headers snippet (again, since it's not inherited)
use "cors_headers"
proxy "http://api-v1-backend1:8080"
proxy "http://api-v1-backend2:8080"
// V1 specific headers
header "X-API-Version" "1.0"
// More restrictive rate limiting for legacy API
limit rate=500 burst=1000
}
if "is_v2_api" {
// Use CORS headers snippet (again, since it's not inherited)
use "cors_headers"
proxy "http://api-v2-backend1:8080"
proxy "http://api-v2-backend2:8080"
proxy "http://api-v2-backend3:8080"
// V2 specific headers
header "X-API-Version" "2.0"
// Higher rate limits for new API
limit rate=2000 burst=4000
}
// Authentication endpoints get special treatment
if "is_auth_request" {
// Use CORS headers snippet (again, since it's not inherited)
use "cors_headers"
// Route to dedicated auth service
proxy "http://auth-service:9090"
// Stricter rate limiting for auth endpoints
limit rate=100 burst=200
// Additional security headers
header "X-Auth-Endpoint" "true"
header "Strict-Transport-Security" "max-age=31536000; includeSubDomains; preload"
}
// Admin IP gets enhanced access
if "is_admin_ip" {
// Use CORS headers snippet (again, since it's not inherited)
use "cors_headers"
// No rate limiting for admin IPs
limit #false
// Admin-specific headers
header "X-Admin-API-Access" "true"
// Enhanced logging
log "/var/log/ferron/api-admin.log"
}
// Health checking
lb_health_check
lb_health_check_max_fails 3
// Proxy settings
proxy_keepalive
proxy_request_header_replace "X-Real-IP" "{client_ip}"
proxy_request_header "X-Forwarded-Proto" "{scheme}"
// Health check endpoint
status 200 url="/health" body="OK"
// Version info endpoint
status 200 url="/version" body="{\"api_versions\":[\"v1\",\"v2\"],\"server\":\"Ferron\"}"
}
// Development subdomain with environment-based configuration
dev.example.com {
// Basic TLS
auto_tls
auto_tls_contact "dev@example.com"
// Use security headers but with relaxed settings
use "security_headers"
// Import reusable condition
use "admin_ip_condition"
// Override some security headers for development
header "X-Frame-Options" "SAMEORIGIN"
// Enhanced logging
log "/var/log/ferron/dev.access.log"
error_log "/var/log/ferron/dev.error.log"
// Development-specific conditions
condition "is_hot_reload" {
is_regex "{path}" "^/(sockjs-node|__webpack_hmr)"
}
condition "is_source_map" {
is_regex "{path}" "\\.map(?:$|[?#])"
}
// Hot reload support
if "is_hot_reload" {
// Non-inherited headers, again
use "security_headers"
header "X-Frame-Options" "SAMEORIGIN"
proxy "http://dev-hmr:3001"
// WebSocket support
proxy_request_header "Connection" "Upgrade"
proxy_request_header "Upgrade" "websocket"
// No caching for hot reload
header "Cache-Control" "no-cache"
}
// Source maps handling
if "is_source_map" {
// Only allow source maps for admin IPs
if "is_admin_ip" {
root "/var/www/dev/sourcemaps"
}
if_not "is_admin_ip" {
status 404 body="Not found"
}
}
// Test endpoints with conditional responses
if "is_admin_ip" {
status 200 url="/test" body="Development server is working (Admin Access)"
status 200 url="/debug" body="{\"client_ip\":\"{client_ip}\",\"method\":\"{method}\",\"path\":\"{path}\"}"
}
if_not "is_admin_ip" {
status 200 url="/test" body="Development server is working"
}
// Default proxy to development backend
proxy "http://dev-backend:3000"
proxy_request_header "X-Dev-Mode" "true"
proxy_request_header "X-Environment" "development"
// Relaxed rate limiting
limit rate=1000 burst=5000
}
// Static content CDN with intelligent caching
cdn.example.com {
// TLS configuration
tls "/etc/ssl/certs/cdn.example.com.crt" "/etc/ssl/private/cdn.example.com.key"
// Static file serving
root "/var/www/cdn"
directory_listing #false
// Use static caching snippet
use "static_caching"
// Import reusable condition
use "admin_ip_condition"
// Conditional caching based on file type
condition "is_image" {
is_regex "{path}" "\\.(png|jpg|jpeg|gif|webp|svg)(?:$|[?#])" case_insensitive=#true
}
condition "is_font" {
is_regex "{path}" "\\.(woff|woff2|ttf|eot|otf)(?:$|[?#])" case_insensitive=#true
}
condition "is_media" {
is_regex "{path}" "\\.(mp4|webm|ogg|mp3|wav)(?:$|[?#])" case_insensitive=#true
}
// Image-specific settings
if "is_image" {
// Extra long caching for images
file_cache_control "public, max-age=2592000, immutable"
// Image-specific headers
header "X-Content-Type" "image"
}
// Font-specific settings
if "is_font" {
// CORS for web fonts
header "Access-Control-Allow-Origin" "*"
header "Access-Control-Allow-Methods" "GET, HEAD, OPTIONS"
// Font-specific caching
file_cache_control "public, max-age=31536000, immutable"
}
// Media-specific settings
if "is_media" {
// Partial content support for media
header "Accept-Ranges" "bytes"
// Media-specific caching
file_cache_control "public, max-age=604800"
}
// Admin IP gets special access
if "is_admin_ip" {
// Allow directory listing for admin IPs
directory_listing #true
// Admin headers
header "X-Admin-CDN-Access" "true"
}
// No rate limiting for CDN
limit #false
// Geographic optimization (example condition)
condition "is_european_request" {
is_regex "{header:CF-IPCountry}" "^(DE|FR|GB|IT|ES|NL|BE|CH|AT|SE|NO|DK|FI|PL)$"
}
if "is_european_request" {
header "X-CDN-Region" "Europe"
// Could proxy to European CDN nodes here
}
if_not "is_european_request" {
header "X-CDN-Region" "Global"
}
}