@@ -108,9 +108,10 @@ jobs:
|
|||||||
done
|
done
|
||||||
|
|
||||||
# ---- DEBIAN/control --------------------------------------------------
|
# ---- DEBIAN/control --------------------------------------------------
|
||||||
# Minimum metadata dpkg requires. We don't declare runtime Depends:
|
# Minimum metadata dpkg requires. The .deb bundles every shared library
|
||||||
# the .deb bundles every shared library nginx needs (see the ldd
|
# nginx links against (see the ldd loop above), so the only Depends we
|
||||||
# loop above), so the only thing the host must provide is glibc.
|
# declare is libjemalloc2 — the systemd unit LD_PRELOADs it for the
|
||||||
|
# nginx workers; without it, the unit would fail to start.
|
||||||
sudo mkdir -p "${DEB_DIR}"
|
sudo mkdir -p "${DEB_DIR}"
|
||||||
sudo tee "${DEB_DIR}/control" >/dev/null <<EOF
|
sudo tee "${DEB_DIR}/control" >/dev/null <<EOF
|
||||||
Package: ${PKG_NAME}
|
Package: ${PKG_NAME}
|
||||||
@@ -118,6 +119,7 @@ jobs:
|
|||||||
Section: base
|
Section: base
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Architecture: ${ARCH}
|
Architecture: ${ARCH}
|
||||||
|
Depends: libjemalloc2
|
||||||
Maintainer: Julio <me@julio.al>
|
Maintainer: Julio <me@julio.al>
|
||||||
Description: Nginx L7 DDoS Protection (The-World-Is-Yours), built by RAWeb CI.
|
Description: Nginx L7 DDoS Protection (The-World-Is-Yours), built by RAWeb CI.
|
||||||
EOF
|
EOF
|
||||||
|
|||||||
@@ -69,7 +69,6 @@ nginx -s reload
|
|||||||
|
|
||||||
## Performance
|
## Performance
|
||||||
|
|
||||||
The default config in `static/nginx/nginx.conf` is tuned for shared hosting at 5,000+ vhost scale. Numbers below are realistic ranges from public benchmarks and our own load testing — your mileage will vary with workload.
|
|
||||||
|
|
||||||
### vs. vanilla nginx (same version, default config)
|
### vs. vanilla nginx (same version, default config)
|
||||||
|
|
||||||
@@ -82,51 +81,6 @@ The default config in `static/nginx/nginx.conf` is tuned for shared hosting at 5
|
|||||||
| Compressed-text bandwidth | **−60 to −80%** | unchanged | brotli + gzip enabled in `http {}` |
|
| Compressed-text bandwidth | **−60 to −80%** | unchanged | brotli + gzip enabled in `http {}` |
|
||||||
| WAF, Lua, HTTP/3 | included | not included | needs custom build |
|
| WAF, Lua, HTTP/3 | included | not included | needs custom build |
|
||||||
|
|
||||||
### vs. OpenResty
|
|
||||||
|
|
||||||
| Area | Twiy | OpenResty |
|
|
||||||
|---|---|---|
|
|
||||||
| nginx version | tracks upstream stable (1.30.0) | lags upstream by months while waiting for openresty's bundle release |
|
|
||||||
| TLS backend | AWS-LC (BoringSSL fork) | OpenSSL (or quictls) by default |
|
|
||||||
| Lua stack | upstream `lua-nginx-module` + pinned `lua-resty-core` | OpenResty's vendored fork |
|
|
||||||
| Module surface | ModSecurity v3, naxsi, brotli, geoip2, http_v3, set_misc, headers_more, http-flv, srcache, redis2, testcookie, lrucache, mysql, lock | similar but defined by openresty's bundle |
|
|
||||||
| Distribution | apt repo, single `.deb` | tarball or vendor's apt repo |
|
|
||||||
|
|
||||||
OpenResty is the right choice if you want a curated, all-in-one Lua-centric stack and don't mind being a few nginx releases behind. Twiy is the right choice if you want vanilla nginx's release cadence with a hardened security/performance stack on top.
|
|
||||||
|
|
||||||
### vs. Apache (httpd)
|
|
||||||
|
|
||||||
| Area | Twiy | Apache (event/prefork MPM) |
|
|
||||||
|---|---|---|
|
|
||||||
| Concurrency model | event-driven, single-process-per-core | thread/process-per-connection (event MPM is closer but still heavier) |
|
|
||||||
| Static file req/s (small files, single core) | typically **2–4× higher** | baseline |
|
|
||||||
| Memory per idle connection | **~kB** | **~hundreds of kB** (per worker process/thread) |
|
|
||||||
| TLS handshake CPU | comparable with mod_ssl, **lower** with mod_md off | baseline |
|
|
||||||
| WAF | ModSecurity v3 (libmodsecurity) + naxsi | ModSecurity v2 (mod_security2) common |
|
|
||||||
| HTTP/3 / QUIC | **yes** (AWS-LC) | **no** in stable releases |
|
|
||||||
|
|
||||||
The nginx-vs-Apache static-file gap widens dramatically at high concurrency (10k+ idle keepalive connections): nginx holds them on epoll for kilobytes each; Apache event MPM still allocates significantly more per connection.
|
|
||||||
|
|
||||||
### Where the wins actually come from in this build
|
|
||||||
|
|
||||||
| Source | Yield |
|
|
||||||
|---|---|
|
|
||||||
| AWS-LC (vs vanilla OpenSSL on TLS) | 5–15% handshake CPU saving |
|
|
||||||
| `open_file_cache` (max=200000, inactive=30s) | 2–5× static throughput on a busy 5k-vhost host |
|
|
||||||
| `ssl_session_cache shared:SSL:200m` | huge — first vs resumed handshake is ~10× CPU difference |
|
|
||||||
| OCSP stapling (`ssl_stapling on`) | removes per-handshake OCSP RTT (often 50–200 ms p95) |
|
|
||||||
| `worker_cpu_affinity auto` | ~5% on CPU-bound workloads (cache locality) |
|
|
||||||
| `brotli on` + `gzip on` in `http{}` | 60–80% smaller text responses |
|
|
||||||
| `keepalive_requests 10000` (vs 1000 default) | fewer reconnects under sustained HTTP/2 load |
|
|
||||||
| `client_header_buffer_size 4k` (down from 2M) | drops worst-case memory amplification surface |
|
|
||||||
| `server_names_hash_max_size 32768` | makes 5k+ vhost configs actually parseable |
|
|
||||||
|
|
||||||
### Things this build deliberately does NOT do (yet)
|
|
||||||
|
|
||||||
- No HTTP/3 `listen 443 quic` directive in `static/nginx/live/default` — left to the per-vhost templates so you can opt in selectively.
|
|
||||||
- No ECDSA P-256 certificates (a per-cert decision; ECDSA handshakes are ~3× faster than RSA-2048).
|
|
||||||
- No OS-level sysctl tuning (`net.core.rmem_max` for QUIC, `net.core.somaxconn`, `fs.file-max`) — would belong in the `.deb` postinst or a `/etc/sysctl.d/twiy.conf` shipped with the package; not yet wired up.
|
|
||||||
|
|
||||||
# Support options.
|
# Support options.
|
||||||
|
|
||||||
- No free support for how to do things, please don't spam with questions in discord.
|
- No free support for how to do things, please don't spam with questions in discord.
|
||||||
|
|||||||
+11
-1
@@ -7,7 +7,7 @@ function reqs() {
|
|||||||
apt-get -y install wget zip unzip build-essential libssl-dev curl nano git
|
apt-get -y install wget zip unzip build-essential libssl-dev curl nano git
|
||||||
# apt-get -y install iptables ipset
|
# apt-get -y install iptables ipset
|
||||||
apt-get install libtool pkg-config make cmake automake autoconf golang-go ninja-build -y
|
apt-get install libtool pkg-config make cmake automake autoconf golang-go ninja-build -y
|
||||||
apt-get install libyajl-dev ssdeep zlib1g-dev libxslt1-dev libgd-dev libgeoip-dev liblmdb-dev libfuzzy-dev libmaxminddb-dev liblua5.1-dev libcurl4-openssl-dev libxml2 libxml2-dev mercurial libpcre2-dev libc-ares-dev libre2-dev -y
|
apt-get install libyajl-dev ssdeep zlib1g-dev libxslt1-dev libgd-dev libgeoip-dev liblmdb-dev libfuzzy-dev libmaxminddb-dev liblua5.1-dev libcurl4-openssl-dev libxml2 libxml2-dev mercurial libpcre2-dev libc-ares-dev libre2-dev libzstd-dev libjemalloc2 -y
|
||||||
mkdir -p $LUA_SCRIPTS
|
mkdir -p $LUA_SCRIPTS
|
||||||
}
|
}
|
||||||
function clean_install() {
|
function clean_install() {
|
||||||
@@ -204,6 +204,14 @@ function clean_install() {
|
|||||||
cd /opt/mod/; git clone --recurse-submodules https://github.com/wargio/naxsi.git naxsi
|
cd /opt/mod/; git clone --recurse-submodules https://github.com/wargio/naxsi.git naxsi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# NGX_MOD_ZSTD — Zstandard compression module from tokers. Pinned via
|
||||||
|
# NGX_MOD_ZSTD; tarball pattern (dir name embeds version → cache invalidates
|
||||||
|
# automatically when the pin moves).
|
||||||
|
if [ ! -d /opt/mod/zstd-nginx-module-${NGX_MOD_ZSTD} ]; then
|
||||||
|
cd /opt/mod/; wget https://github.com/tokers/zstd-nginx-module/archive/refs/tags/${NGX_MOD_ZSTD}.tar.gz
|
||||||
|
cd /opt/mod/; tar xf ${NGX_MOD_ZSTD}.tar.gz; rm -Rf ${NGX_MOD_ZSTD}.tar.gz
|
||||||
|
fi
|
||||||
|
|
||||||
# END OF NGINX MODULES
|
# END OF NGINX MODULES
|
||||||
# ============================================================================================================
|
# ============================================================================================================
|
||||||
}
|
}
|
||||||
@@ -261,6 +269,7 @@ test_nginx() {
|
|||||||
--add-module=/opt/mod/srcache-nginx-module-${NGX_MOD_LUA_SRCACHE} \
|
--add-module=/opt/mod/srcache-nginx-module-${NGX_MOD_LUA_SRCACHE} \
|
||||||
--add-module=/opt/mod/redis2-nginx-module \
|
--add-module=/opt/mod/redis2-nginx-module \
|
||||||
--add-module=/opt/mod/ngx_brotli \
|
--add-module=/opt/mod/ngx_brotli \
|
||||||
|
--add-module=/opt/mod/zstd-nginx-module-${NGX_MOD_ZSTD} \
|
||||||
--add-module=/opt/mod/testcookie \
|
--add-module=/opt/mod/testcookie \
|
||||||
--with-cc-opt="-g -O2 -fstack-protector-strong -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fPIC -I/usr/local/aws-lc/include" \
|
--with-cc-opt="-g -O2 -fstack-protector-strong -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fPIC -I/usr/local/aws-lc/include" \
|
||||||
--with-ld-opt="-Wl,-rpath,/usr/local/LuaJIT/lib -Wl,-rpath,/usr/local/lib -Wl,-z,relro -Wl,-z,now -Wl,--as-needed -pie -L/opt/mod/pcre2-${SYSTEM_PCRE}/.libs -lpcre2-8 -L/usr/local/aws-lc/lib -lssl -lcrypto -Wl,-rpath,/usr/local/aws-lc/lib"
|
--with-ld-opt="-Wl,-rpath,/usr/local/LuaJIT/lib -Wl,-rpath,/usr/local/lib -Wl,-z,relro -Wl,-z,now -Wl,--as-needed -pie -L/opt/mod/pcre2-${SYSTEM_PCRE}/.libs -lpcre2-8 -L/usr/local/aws-lc/lib -lssl -lcrypto -Wl,-rpath,/usr/local/aws-lc/lib"
|
||||||
@@ -319,6 +328,7 @@ function build() {
|
|||||||
--add-module=/opt/mod/srcache-nginx-module-${NGX_MOD_LUA_SRCACHE} \
|
--add-module=/opt/mod/srcache-nginx-module-${NGX_MOD_LUA_SRCACHE} \
|
||||||
--add-module=/opt/mod/redis2-nginx-module \
|
--add-module=/opt/mod/redis2-nginx-module \
|
||||||
--add-module=/opt/mod/ngx_brotli \
|
--add-module=/opt/mod/ngx_brotli \
|
||||||
|
--add-module=/opt/mod/zstd-nginx-module-${NGX_MOD_ZSTD} \
|
||||||
--add-module=/opt/mod/testcookie \
|
--add-module=/opt/mod/testcookie \
|
||||||
--with-cc-opt="-g -O2 -fstack-protector-strong -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fPIC -I/usr/local/aws-lc/include" \
|
--with-cc-opt="-g -O2 -fstack-protector-strong -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fPIC -I/usr/local/aws-lc/include" \
|
||||||
--with-ld-opt="-Wl,-rpath,/usr/local/LuaJIT/lib -Wl,-rpath,/usr/local/lib -Wl,-z,relro -Wl,-z,now -Wl,--as-needed -pie -L/opt/mod/pcre2-${SYSTEM_PCRE}/.libs -lpcre2-8 -L/usr/local/aws-lc/lib -lssl -lcrypto -Wl,-rpath,/usr/local/aws-lc/lib"
|
--with-ld-opt="-Wl,-rpath,/usr/local/LuaJIT/lib -Wl,-rpath,/usr/local/lib -Wl,-z,relro -Wl,-z,now -Wl,--as-needed -pie -L/opt/mod/pcre2-${SYSTEM_PCRE}/.libs -lpcre2-8 -L/usr/local/aws-lc/lib -lssl -lcrypto -Wl,-rpath,/usr/local/aws-lc/lib"
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ Wants=network-online.target
|
|||||||
[Service]
|
[Service]
|
||||||
Type=forking
|
Type=forking
|
||||||
PIDFile=/run/nginx.pid
|
PIDFile=/run/nginx.pid
|
||||||
|
# jemalloc replaces glibc malloc — better fragmentation/perf under nginx's
|
||||||
|
# alloc/free churn at scale. Package depends on libjemalloc2 so the .so is
|
||||||
|
# guaranteed present. Removing this line falls back to glibc malloc cleanly.
|
||||||
|
Environment=LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2
|
||||||
ExecStartPre=/usr/bin/install -d -o nginx -g nginx -m 0755 /usr/local/nginx /usr/local/nginx/client_body_temp /usr/local/nginx/proxy_temp /usr/local/nginx/fastcgi_temp /usr/local/nginx/uwsgi_temp /usr/local/nginx/scgi_temp /var/log/nginx
|
ExecStartPre=/usr/bin/install -d -o nginx -g nginx -m 0755 /usr/local/nginx /usr/local/nginx/client_body_temp /usr/local/nginx/proxy_temp /usr/local/nginx/fastcgi_temp /usr/local/nginx/uwsgi_temp /usr/local/nginx/scgi_temp /var/log/nginx
|
||||||
ExecStartPre=/usr/sbin/nginx -t
|
ExecStartPre=/usr/sbin/nginx -t
|
||||||
ExecStart=/usr/sbin/nginx
|
ExecStart=/usr/sbin/nginx
|
||||||
|
|||||||
+14
-17
@@ -8,7 +8,7 @@
|
|||||||
user nginx;
|
user nginx;
|
||||||
pid /var/run/nginx.pid;
|
pid /var/run/nginx.pid;
|
||||||
worker_processes auto;
|
worker_processes auto;
|
||||||
worker_cpu_affinity auto; # Pin workers to cores for L1/L2 locality.
|
worker_cpu_affinity auto;
|
||||||
worker_rlimit_nofile 65535;
|
worker_rlimit_nofile 65535;
|
||||||
|
|
||||||
events {
|
events {
|
||||||
@@ -32,8 +32,6 @@ http {
|
|||||||
# =================== END LOGS ========================= #
|
# =================== END LOGS ========================= #
|
||||||
|
|
||||||
# ==================== GENERAL ========================= #
|
# ==================== GENERAL ========================= #
|
||||||
# Header buffers — keep small. The previous 2M default was a memory
|
|
||||||
# amplification target (per-conn × worker_connections = absurd worst case).
|
|
||||||
client_header_buffer_size 4k;
|
client_header_buffer_size 4k;
|
||||||
large_client_header_buffers 4 16k;
|
large_client_header_buffers 4 16k;
|
||||||
client_body_buffer_size 16k;
|
client_body_buffer_size 16k;
|
||||||
@@ -41,10 +39,10 @@ http {
|
|||||||
client_body_timeout 30s;
|
client_body_timeout 30s;
|
||||||
client_header_timeout 30s;
|
client_header_timeout 30s;
|
||||||
send_timeout 30s;
|
send_timeout 30s;
|
||||||
reset_timedout_connection on; # Free sockets fast under churn.
|
reset_timedout_connection on;
|
||||||
keepalive_timeout 65s; # Amortise TCP setup across requests.
|
keepalive_timeout 65s;
|
||||||
keepalive_requests 10000; # Default 1000 too low for HTTP/2.
|
keepalive_requests 2000;
|
||||||
max_headers 100; # nginx 1.29.8 — slowloris defence.
|
max_headers 100;
|
||||||
port_in_redirect off;
|
port_in_redirect off;
|
||||||
sendfile on;
|
sendfile on;
|
||||||
sendfile_max_chunk 1m;
|
sendfile_max_chunk 1m;
|
||||||
@@ -53,8 +51,6 @@ http {
|
|||||||
server_tokens off;
|
server_tokens off;
|
||||||
server_name_in_redirect off;
|
server_name_in_redirect off;
|
||||||
|
|
||||||
# 5,000+ vhost hash sizing. _max_size must exceed total server names;
|
|
||||||
# _bucket_size must be a CPU-cache-line multiple (32/64/128/256/512/1024).
|
|
||||||
server_names_hash_bucket_size 128;
|
server_names_hash_bucket_size 128;
|
||||||
server_names_hash_max_size 32768;
|
server_names_hash_max_size 32768;
|
||||||
types_hash_max_size 4096;
|
types_hash_max_size 4096;
|
||||||
@@ -67,14 +63,13 @@ http {
|
|||||||
|
|
||||||
# ===================== TLS ============================ #
|
# ===================== TLS ============================ #
|
||||||
ssl_protocols TLSv1.2 TLSv1.3;
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
ssl_prefer_server_ciphers off; # TLS 1.3 ciphers, client picks.
|
ssl_prefer_server_ciphers off;
|
||||||
ssl_session_cache shared:SSL:200m; # ~800k sessions shared across workers
|
ssl_session_cache shared:SSL:200m;
|
||||||
ssl_session_timeout 1d;
|
ssl_session_timeout 1d;
|
||||||
ssl_session_tickets off; # Off unless you have ticket-key rotation.
|
ssl_session_tickets off;
|
||||||
ssl_stapling on; # OCSP stapling — avoid per-handshake OCSP lookups.
|
ssl_stapling on;
|
||||||
ssl_stapling_verify on;
|
ssl_stapling_verify on;
|
||||||
# ===================== END TLS ======================== #
|
# ===================== END TLS ======================== #
|
||||||
|
|
||||||
resolver 1.1.1.1 1.0.0.1 valid=300s;
|
resolver 1.1.1.1 1.0.0.1 valid=300s;
|
||||||
resolver_timeout 5s;
|
resolver_timeout 5s;
|
||||||
default_type application/octet-stream;
|
default_type application/octet-stream;
|
||||||
@@ -86,9 +81,6 @@ http {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# ==================== COMPRESSION ===================== #
|
# ==================== COMPRESSION ===================== #
|
||||||
# Compiled in, now actually enabled. Bandwidth saving on text responses
|
|
||||||
# is typically 60-80% for HTML/JSON/CSS/JS/SVG. Comp level 4 is the
|
|
||||||
# sweet spot for CPU vs ratio on shared hosting.
|
|
||||||
gzip on;
|
gzip on;
|
||||||
gzip_vary on;
|
gzip_vary on;
|
||||||
gzip_proxied any;
|
gzip_proxied any;
|
||||||
@@ -100,6 +92,11 @@ http {
|
|||||||
brotli_comp_level 4;
|
brotli_comp_level 4;
|
||||||
brotli_min_length 256;
|
brotli_min_length 256;
|
||||||
brotli_types text/plain text/css text/xml application/json application/javascript application/xml application/xml+rss application/atom+xml image/svg+xml font/ttf font/otf font/woff font/woff2;
|
brotli_types text/plain text/css text/xml application/json application/javascript application/xml application/xml+rss application/atom+xml image/svg+xml font/ttf font/otf font/woff font/woff2;
|
||||||
|
|
||||||
|
zstd on;
|
||||||
|
zstd_comp_level 4;
|
||||||
|
zstd_min_length 256;
|
||||||
|
zstd_types text/plain text/css text/xml application/json application/javascript application/xml application/xml+rss application/atom+xml image/svg+xml font/ttf font/otf font/woff font/woff2;
|
||||||
# =================== END COMPRESSION ================== #
|
# =================== END COMPRESSION ================== #
|
||||||
# =================== END GENERAL ====================== #
|
# =================== END GENERAL ====================== #
|
||||||
|
|
||||||
|
|||||||
@@ -52,3 +52,8 @@ export NGX_MOD_LUA_LOCK="0.09"
|
|||||||
|
|
||||||
# https://github.com/openresty/srcache-nginx-module/tags
|
# https://github.com/openresty/srcache-nginx-module/tags
|
||||||
export NGX_MOD_LUA_SRCACHE="0.33"
|
export NGX_MOD_LUA_SRCACHE="0.33"
|
||||||
|
|
||||||
|
# https://github.com/tokers/zstd-nginx-module/tags
|
||||||
|
# Zstandard compression module. Chrome 123+ and Firefox 126+ send
|
||||||
|
# `Accept-Encoding: zstd`; older clients fall back to brotli/gzip.
|
||||||
|
export NGX_MOD_ZSTD="0.1.1"
|
||||||
|
|||||||
Reference in New Issue
Block a user