{"id":16366352,"url":"https://github.com/centminmod/caddy-forward-proxy","last_synced_at":"2026-03-18T19:12:24.262Z","repository":{"id":255869742,"uuid":"853294594","full_name":"centminmod/caddy-forward-proxy","owner":"centminmod","description":null,"archived":false,"fork":false,"pushed_at":"2024-09-09T14:15:49.000Z","size":53,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-12-28T18:48:57.566Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/centminmod.png","metadata":{"files":{"readme":"readme-normal-vhost-relro.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-09-06T11:24:58.000Z","updated_at":"2024-09-09T14:15:53.000Z","dependencies_parsed_at":"2024-09-09T17:06:29.678Z","dependency_job_id":null,"html_url":"https://github.com/centminmod/caddy-forward-proxy","commit_stats":null,"previous_names":["centminmod/caddy-forward-proxy"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/centminmod/caddy-forward-proxy","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/centminmod%2Fcaddy-forward-proxy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/centminmod%2Fcaddy-forward-proxy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/centminmod%2Fcaddy-forward-proxy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/centminmod%2Fcaddy-forward-proxy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/centminmod","download_url":"https://codeload.github.com/centminmod/caddy-forward-proxy/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/centminmod%2Fcaddy-forward-proxy/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28985818,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-01T18:17:03.387Z","status":"ssl_error","status_checked_at":"2026-02-01T18:16:57.287Z","response_time":56,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-11T02:46:08.410Z","updated_at":"2026-02-01T18:32:34.801Z","avatar_url":"https://github.com/centminmod.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"A guide on building Caddy with the `forwardproxy` plugin and hardened compiler options on [Centmin Mod LEMP stack server](https://centminmod.com) running AlmaLinux 9.\n\n### Step 1: Install Dependencies\nFirst, ensure you have the necessary tools for building software, as well as Go, which is required to build Caddy.\n\n1. **Update your system**:\n   ```bash\n   sudo dnf update -y\n   sudo dnf install clang llvm-devel lld -y\n   ```\n   ```bash\n   clang --version\n   clang version 17.0.6 (AlmaLinux OS Foundation 17.0.6-5.el9)\n   Target: x86_64-redhat-linux-gnu\n   Thread model: posix\n   InstalledDir: /usr/bin\n   ```\n\n2. **Install Go**:\n   Download and install the latest Go version (replace with the latest version link if necessary).\n\n   ```\n   export CC=clang\n   export CXX=clang++\n\n   /usr/local/src/centminmod/addons/golang.sh install\n   grep -qxF 'export PATH=$PATH:/root/go/bin' ~/.bashrc || echo 'export PATH=$PATH:/root/go/bin' \u003e\u003e ~/.bashrc\n   source /root/.bashrc\n   ```\n\n   Verify Go installation:\n   ```bash\n   go version\n   ```\n   ```\n   go version\n   go version go1.23.1 linux/amd64\n   ```\n\n### Step 2: Build Caddy with Forward Proxy Plugin\n\n1. **Install xcaddy**:\n   `xcaddy` is a command-line tool that makes it easy to build custom versions of Caddy with plugins. Install it with the following command:\n\n   ```bash\n   go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest\n   ```\n\n2. **Build Caddy with `forwardproxy` Plugin**:\n   Now, use `xcaddy` to build Caddy with the `forwardproxy` plugin:\n\n   ```bash\n   mkdir -p /home/caddybuild\n   cd /home/caddybuild\n   # Set compiler and linker flags for enhanced security\n   export CGO_CFLAGS=\"-O2 -fstack-protector-strong -D_FORTIFY_SOURCE=2\"\n   export CGO_LDFLAGS=\"-Wl,-z,relro,-z,now -fuse-ld=lld\"\n   export GOFLAGS=\"-buildmode=pie\"\n   CGO_ENABLED=1 CC=clang CXX=clang++ xcaddy build --with github.com/caddyserver/forwardproxy@latest\n   strip caddy\n   ```\n\n   This will download the Caddy source code and build it with the forward proxy plugin.\n\n   For upgrades:\n\n   ```bash\n    caddy upgrade\n    2024/09/07 13:56:49.811 INFO    this executable will be replaced        {\"path\": \"/usr/local/bin/caddy\"}\n    2024/09/07 13:56:49.811 INFO    requesting build        {\"os\": \"linux\", \"arch\": \"amd64\", \"packages\": [\"github.com/caddyserver/forwardproxy\"]}\n    2024/09/07 13:56:49.881 INFO    build acquired; backing up current executable   {\"current_path\": \"/usr/local/bin/caddy\", \"backup_path\": \"/usr/local/bin/caddy.tmp\"}\n    2024/09/07 13:56:49.881 INFO    downloading binary      {\"destination\": \"/usr/local/bin/caddy\"}\n    2024/09/07 13:56:50.335 INFO    download successful; displaying new binary details      {\"location\": \"/usr/local/bin/caddy\"}\n\n    Module versions:\n\n    http.handlers.forward_proxy v0.0.0-20240718200834-02be81e69669\n\n      Non-standard modules: 1\n\n      Unknown modules: 0\n\n    Version:\n    v2.8.4 h1:q3pe0wpBj1OcHFZ3n/1nl4V4bxBrYoSoab7rL9BMYNk=\n\n    2024/09/07 13:56:50.396 INFO    upgrade successful; please restart any running Caddy instances  {\"executable\": \"/usr/local/bin/caddy\"}\n   ```\n\n3. **Setup Caddy**:\n   Once the build is complete, move the Caddy binary to `/usr/local/bin`:\n\n   ```bash\n   sudo mv -f caddy /usr/local/bin/caddy\n   sudo chmod +x /usr/local/bin/caddy\n   ls -lah $(which caddy)\n   ```\n\n   You can verify Caddy with:\n\n   ```bash\n   caddy version\n   ```\n   ```\n   caddy version\n   v2.8.4 h1:q3pe0wpBj1OcHFZ3n/1nl4V4bxBrYoSoab7rL9BMYNk=\n   ```\n\n   Create `caddy` user:\n   ```bash\n   sudo useradd -r -d /home/caddy -s /sbin/nologin caddy\n   ```\n\n   Setup Caddy directories:\n   ```bash\n   sudo mkdir -p /home/caddy/.config/caddy\n   sudo mkdir -p /home/caddy/.local/share/caddy/locks\n   sudo chown -R caddy:caddy /home/caddy\n   sudo chmod 755 /home/caddy\n   ```\n\n4. **Setup Logging \u0026 Logrotation**:\n   Setup logging directory and logrotation:\n\n   ```bash\n   sudo mkdir -p /var/log/caddy\n   sudo chown -R caddy:caddy /var/log/caddy\n   sudo chmod 755 /var/log/caddy\n   ```\n   ```bash\n   cat \u003e /etc/logrotate.d/caddy \u003c\u003cEOF\n   /var/log/caddy/*.log {\n       daily\n       missingok\n       rotate 7\n       compress\n       delaycompress\n       notifempty\n       create 0640 caddy caddy\n       sharedscripts\n       postrotate\n           systemctl reload caddy \u003e /dev/null 2\u003e/dev/null || true\n       endscript\n   }\n   EOF\n   ```\n\n### Step 3: Configure Caddy with Forward Proxy\n\n1. **Create Caddyfile**:\n   Create a `Caddyfile` configuration file to set up the proxy. You can place this file in `/etc/caddy/Caddyfile`.\n\n   ```bash\n   sudo mkdir -p /etc/caddy\n   sudo mkdir -p /etc/caddy/ssl/domain.com /home/caddy/domains/domain.com/public\n   sudo nano /etc/caddy/Caddyfile\n   ```\n   ```bash\n   sudo cp /usr/local/nginx/conf/ssl/domain.com/domain.com.crt /etc/caddy/ssl/domain.com/\n   sudo cp /usr/local/nginx/conf/ssl/domain.com/domain.com.key /etc/caddy/ssl/domain.com/\n \n   sudo chown caddy:caddy /etc/caddy/ssl/domain.com/domain.com.crt\n   sudo chown caddy:caddy /etc/caddy/ssl/domain.com/domain.com.key\n \n   sudo chmod 600 /etc/caddy/ssl/domain.com/domain.com.crt\n   sudo chmod 600 /etc/caddy/ssl/domain.com/domain.com.key\n\n   sudo cp -a /home/nginx/domains/domain.com/public/* /home/caddy/domains/domain.com/public\n   sudo chown -R caddy:caddy /home/caddy/domains/domain.com/public\n   ```\n\n   Here is an example of a `Caddyfile` configured for forward proxying with non-HTTPS:\n\n```caddy\n{\n        log {\n                output file /var/log/caddy/caddy_errors.log\n                level ERROR\n        }\n        auto_https off\n}\n\n:8081 {\n        route {\n                forward_proxy {\n                        basic_auth yourusername yourpassword\n                        hide_ip\n                        hide_via\n                        probe_resistance secret_token\n                }\n        }\n        log {\n                output file /var/log/caddy/forward_proxy_access_8081.log\n                format json\n                level INFO\n        }\n}\n\ndomain.com:8088 {\n        root * /home/caddy/domains/domain.com/public\n        encode gzip\n\n        #php_fastcgi 127.0.0.1:9000 {\n            #split .php\n            #pool www\n        #}\n        file_server {\n          #precompressed gzip\n          hide .git\n        }\n\n        header Server \"caddy centminmod\"\n        header X-Powered-By \"caddy\"\n        header X-Xss-Protection \"1; mode=block\"\n        header X-Content-Type-Options \"nosniff\"\n        header ?Accept-Ranges bytes\n        header Content-Type \"text/html; charset=utf-8\"\n        header Connection \"keep-alive\"\n        #header Vary \"Accept-Encoding\"\n\n        log {\n                output file /var/log/caddy/domain.com_access.log\n                format json\n                level INFO\n        }\n}\n\ndomain.com:8443 {\n        root * /home/caddy/domains/domain.com/public\n        encode gzip\n\n        #php_fastcgi 127.0.0.1:9000 {\n            #split .php\n            #pool www\n        #}\n        file_server {\n          #precompressed gzip\n          hide .git\n        }\n\n        # TLS configuration using Nginx certificates\n        tls /etc/caddy/ssl/domain.com/domain.com.crt /etc/caddy/ssl/domain.com/domain.com.key  {\n          ciphers TLS_AES_128_GCM_SHA256 TLS_CHACHA20_POLY1305_SHA256 TLS_AES_256_GCM_SHA384 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256\n          curves x25519 secp256r1\n        }\n\n        header Server \"caddy centminmod\"\n        header X-Powered-By \"caddy\"\n        header X-Xss-Protection \"1; mode=block\"\n        header X-Content-Type-Options \"nosniff\"\n        header ?Accept-Ranges bytes\n        header Content-Type \"text/html; charset=utf-8\"\n        header Connection \"keep-alive\"\n        #header Vary \"Accept-Encoding\"\n\n        log {\n                output file /var/log/caddy/domain.com_tls_access.log\n                format json\n                level INFO\n        }\n}\n```\n\n   This configuration makes Caddy listen on port 81 and act as a forward proxy and dummy non-HTTPS and HTTPS vhost.\n\n```bash\ncurl -Ik https://domain.com:8443\nHTTP/2 200 \naccept-ranges: bytes\nalt-svc: h3=\":8443\"; ma=2592000\ncontent-type: text/html; charset=utf-8\netag: \"d3ywcl78i2wg4wk\"\nlast-modified: Fri, 06 Sep 2024 03:55:25 GMT\nserver: caddy centminmod\nvary: Accept-Encoding\nx-content-type-options: nosniff\nx-powered-by: caddy\nx-xss-protection: 1; mode=block\ncontent-length: 6356\ndate: Sat, 07 Sep 2024 19:32:46 GMT\n\ncurl -Ik http://domain.com:8088\nHTTP/1.1 200 OK\nConnection: keep-alive\nContent-Type: text/html; charset=utf-8\nServer: caddy centminmod\nX-Content-Type-Options: nosniff\nX-Powered-By: caddy\nX-Xss-Protection: 1; mode=block\nDate: Sat, 07 Sep 2024 17:13:21 GMT\n```\n```bash\ncurl -Ik https://domain.com:443\nHTTP/2 200 \ndate: Sat, 07 Sep 2024 17:14:13 GMT\ncontent-type: text/html; charset=utf-8\ncontent-length: 6356\nlast-modified: Fri, 06 Sep 2024 03:55:25 GMT\nvary: Accept-Encoding\netag: \"66da7d2d-18d4\"\nserver: nginx centminmod\nx-powered-by: centminmod\nx-xss-protection: 1; mode=block\nx-content-type-options: nosniff\naccept-ranges: bytes\n\ncurl -Ik http://domain.com:80\nHTTP/1.1 200 OK\nDate: Sat, 07 Sep 2024 17:14:18 GMT\nContent-Type: text/html; charset=utf-8\nContent-Length: 6356\nLast-Modified: Fri, 06 Sep 2024 03:55:25 GMT\nConnection: keep-alive\nVary: Accept-Encoding\nETag: \"66da7d2d-18d4\"\nServer: nginx centminmod\nX-Powered-By: centminmod\nX-Xss-Protection: 1; mode=block\nX-Content-Type-Options: nosniff\nAccept-Ranges: bytes\n```\n\n```\nnginx -V\nnginx version: nginx/1.27.1 (060924-044706-almalinux9-kvm-95a2688)\nbuilt by gcc 13.3.1 20240611 (Red Hat 13.3.1-2) (GCC) \nbuilt with OpenSSL 1.1.1 (compatible; AWS-LC 1.34.2) (running with AWS-LC 1.34.2)\nTLS SNI support enabled\n```\n\u003e configure arguments: --with-ld-opt='-Wl,-E -L/usr/local/zlib-cf/lib -L/opt/aws-lc-install/lib64 -lcrypto -lssl -L/usr/local/nginx-dep/lib -lrt -ljemalloc -Wl,-z,relro,-z,now -Wl,-rpath,/usr/local/zlib-cf/lib:/opt/aws-lc-install/lib64:/usr/local/nginx-dep/lib -pie -flto=2 -flto-compression-level=1 -fuse-ld=gold' --with-cc-opt='-I/opt/aws-lc-install/include -I/usr/local/zlib-cf/include -I/usr/local/nginx-dep/include -m64 -march=native -fPIC -g -O3 -fstack-protector-strong -flto=2 -flto-compression-level=1 -fuse-ld=gold --param=ssp-buffer-size=4 -Wformat -Wno-pointer-sign -Wimplicit-fallthrough=0 -Wno-implicit-function-declaration -Wno-cast-align -Wno-builtin-declaration-mismatch -Wno-deprecated-declarations -Wno-int-conversion -Wno-unused-result -Wno-vla-parameter -Wno-maybe-uninitialized -Wno-return-local-addr -Wno-array-parameter -Wno-alloc-size-larger-than -Wno-address -Wno-array-bounds -Wno-discarded-qualifiers -Wno-stringop-overread -Wno-stringop-truncation -Wno-missing-field-initializers -Wno-unused-variable -Wno-format -Wno-error=unused-result -Wno-missing-profile -Wno-stringop-overflow -Wno-free-nonheap-object -Wno-discarded-qualifiers -Wno-bad-function-cast -Wno-dangling-pointer -Wno-array-parameter -fcode-hoisting -Wno-cast-function-type -Wno-format-extra-args -Wp,-D_FORTIFY_SOURCE=2' --prefix=/usr/local/nginx --sbin-path=/usr/local/sbin/nginx --conf-path=/usr/local/nginx/conf/nginx.conf --build=060924-044706-almalinux9-kvm-95a2688 --with-compat --without-pcre2 --with-http_stub_status_module --with-http_secure_link_module --with-libatomic --with-http_gzip_static_module --with-http_sub_module --with-http_addition_module --with-http_image_filter_module=dynamic --with-http_geoip_module --with-stream_geoip_module --with-stream_realip_module --with-stream_ssl_preread_module --with-threads --with-stream --with-stream_ssl_module --with-http_realip_module --add-dynamic-module=../ngx-fancyindex-0.5.2 --add-module=../ngx_cache_purge-2.5.3 --add-dynamic-module=../ngx_devel_kit-0.3.2 --add-dynamic-module=../set-misc-nginx-module-0.33 --add-dynamic-module=../echo-nginx-module-0.63 --add-module=../redis2-nginx-module-0.15 --add-module=../ngx_http_redis-0.4.0-cmm --add-module=../memc-nginx-module-0.20 --add-module=../srcache-nginx-module-0.33 --add-dynamic-module=../headers-more-nginx-module-0.37 --with-pcre-jit --with-zlib=../zlib-cloudflare-1.3.3 --with-zlib-opt=-fPIC --with-http_ssl_module --with-http_v2_module --with-http_v3_module\n\n```\ncaddy version\nv2.8.4 h1:q3pe0wpBj1OcHFZ3n/1nl4V4bxBrYoSoab7rL9BMYNk=\n```\n\n## h2load benchmarks\n\n### Nginx vs Caddy HTTPS Performance Comparison\n\n| Metric                   | Nginx (c200, n5000, m100) | Caddy (c200, n5000, m100) | Nginx (c300, n5000, m100) | Caddy (c300, n5000, m100) | Nginx (c500, n10000, m100) | Caddy (c500, n10000, m100) | Nginx (c600, n50000, m100) | Caddy (c600, n50000, m100) |\n|--------------------------|---------------------------|---------------------------|---------------------------|---------------------------|----------------------------|----------------------------|----------------------------|----------------------------|\n| Total Time               | 136.36ms                  | 135.66ms                  | 152.96ms                  | 160.81ms                  | 271.58ms                   | 304.81ms                   | 1.11s                      | 1.04s                      |\n| Requests per second      | 36,668.18                 | 36,857.66                 | 32,689.35                 | 31,091.82                 | 36,821.97                  | 32,807.21                  | 44,867.03                  | 47,899.33                  |\n| Data transfer rate       | 79.27MB/s                 | 74.87MB/s                 | 70.70MB/s                 | 63.32MB/s                 | 79.62MB/s                  | 66.72MB/s                  | 96.94MB/s                  | 96.95MB/s                  |\n| Total traffic            | 10.81MB                   | 10.16MB                   | 10.81MB                   | 10.18MB                   | 21.62MB                    | 20.34MB                    | 108.03MB                   | 101.20MB                   |\n| Header size              | 1010.74KB                 | 114.38KB                  | 1010.74KB                 | 136.45KB                  | 1.97MB                     | 248.54KB                   | 9.87MB                     | 857.04KB                   |\n| Header space savings     | 26.86%                    | 93.62%                    | 26.86%                    | 92.39%                    | 26.86%                     | 93.07%                     | 26.86%                     | 95.22%                     |\n| Data size                | 9.73MB                    | 9.95MB                    | 9.73MB                    | 9.95MB                    | 19.45MB                    | 19.89MB                    | 97.27MB                    | 99.47MB                    |\n| Min request time         | 11.28ms                   | 1.34ms                    | 1.49ms                    | 514µs                     | 8.44ms                     | 247µs                      | 29.57ms                    | 486µs                      |\n| Max request time         | 96.89ms                   | 105.81ms                  | 92.72ms                   | 131.07ms                  | 122.85ms                   | 241.83ms                   | 787.24ms                   | 974.05ms                   |\n| Mean request time        | 43.93ms                   | 40.40ms                   | 39.64ms                   | 63.51ms                   | 55.67ms                    | 78.92ms                    | 329.38ms                   | 327.67ms                   |\n| Request time std dev     | 22.94ms                   | 21.30ms                   | 19.19ms                   | 27.13ms                   | 24.41ms                    | 58.16ms                    | 189.16ms                   | 280.40ms                   |\n| Min connect time         | 5.48ms                    | 8.68ms                    | 7.50ms                    | 10.42ms                   | 11.22ms                    | 17.65ms                    | 22.73ms                    | 26.56ms                    |\n| Max connect time         | 38.78ms                   | 71.80ms                   | 110.90ms                  | 114.19ms                  | 161.34ms                   | 270.80ms                   | 381.14ms                   | 1.02s                      |\n| Mean connect time        | 20.52ms                   | 37.17ms                   | 41.50ms                   | 41.96ms                   | 71.70ms                    | 90.03ms                    | 196.48ms                   | 271.04ms                   |\n| Connect time std dev     | 8.88ms                    | 14.06ms                   | 20.74ms                   | 13.20ms                   | 42.37ms                    | 47.32ms                    | 137.78ms                   | 251.16ms                   |\n| Min time to 1st byte     | 25.20ms                   | 47.90ms                   | 38.11ms                   | 40.55ms                   | 46.95ms                    | 70.36ms                    | 52.83ms                    | 103.26ms                   |\n| Max time to 1st byte     | 131.55ms                  | 134.20ms                  | 144.60ms                  | 159.51ms                  | 254.00ms                   | 299.01ms                   | 1.09s                      | 1.03s                      |\n| Mean time to 1st byte    | 62.99ms                   | 73.23ms                   | 80.25ms                   | 94.68ms                   | 126.03ms                   | 153.86ms                   | 520.12ms                   | 537.79ms                   |\n| Time to 1st byte std dev | 30.20ms                   | 24.47ms                   | 29.36ms                   | 29.95ms                   | 56.04ms                    | 62.06ms                    | 294.40ms                   | 266.34ms                   |\n| Min requests/sec         | 184.58                    | 184.84                    | 114.24                    | 101.51                    | 77.79                      | 66.35                      | 75.20                      | 79.98                      |\n| Max requests/sec         | 982.30                    | 519.99                    | 418.33                    | 393.76                    | 424.24                     | 283.74                     | 1569.15                    | 810.41                     |\n| Mean requests/sec        | 480.92                    | 326.53                    | 230.91                    | 145.70                    | 196.66                     | 122.00                     | 283.74                     | 126.96                     |\n| Requests/sec std dev     | 236.54                    | 109.99                    | 78.24                     | 52.68                     | 102.71                     | 59.88                      | 313.41                     | 85.16                      |\n| TLS Protocol             | TLSv1.3                   | TLSv1.3                   | TLSv1.3                   | TLSv1.3                   | TLSv1.3                    | TLSv1.3                    | TLSv1.3                    | TLSv1.3                    |\n| Cipher                   | TLS_AES_256_GCM_SHA384    | TLS_AES_128_GCM_SHA256    | TLS_AES_256_GCM_SHA384    | TLS_AES_128_GCM_SHA256    | TLS_AES_256_GCM_SHA384     | TLS_AES_128_GCM_SHA256     | TLS_AES_256_GCM_SHA384     | TLS_AES_128_GCM_SHA256     |\n| Server Temp Key          | X25519 253 bits           | X25519 253 bits           | X25519 253 bits           | X25519 253 bits           | X25519 253 bits            | X25519 253 bits            | X25519 253 bits            | X25519 253 bits            |\n| Application protocol     | h2                        | h2                        | h2                        | h2                        | h2                         | h2                         | h2                         | h2                         |\n\n# Nginx vs Caddy Binary Security and Performance Comparison\n\n### Security Features Comparison\n\n| Feature          | Nginx            | Caddy            | Performance Impact                                |\n|------------------|------------------|------------------|---------------------------------------------------|\n| RELRO            | Full             | Partial          | Minimal impact, slight load time increase for full|\n| Canary           | Yes              | No               | Slight performance overhead for canary            |\n| NX               | Yes              | Yes              | Negligible impact                                 |\n| PIE              | Yes              | Yes              | Small performance overhead for PIE                |\n| Clang CFI        | No               | No               | N/A                                               |\n| SafeStack        | No               | No               | N/A                                               |\n| RPATH            | No               | No               | N/A                                               |\n| RUNPATH          | Yes              | No               | Negligible impact                                 |\n| Symbols          | No               | No               | N/A (stripped binaries are smaller)               |\n| Fortify Source   | Yes              | Yes              | Minimal performance impact                        |\n| Fortified        | 4                | 2                | Minimal performance impact                        |\n| Fortify-able     | 11               | 2                | N/A                                               |\n\n## Analysis and Implications\n\n1. **RELRO (RELocation Read-Only)**: \n   - Nginx has full RELRO, while Caddy now has partial RELRO.\n   - Full RELRO provides better security against certain types of attacks, but partial RELRO is still an improvement.\n   - Performance impact: Minimal difference between full and partial RELRO.\n\n2. **Stack Canary**:\n   - Nginx uses stack canaries, Caddy still does not.\n   - Canaries add a small overhead but provide protection against stack buffer overflows.\n   - Performance impact: Nginx might have a slight performance penalty due to canary checks.\n   - Note: Go (used by Caddy) has built-in memory safety features that may provide similar protections through different mechanisms.\n\n3. **NX (No-Execute)**:\n   - Both binaries have NX enabled, which is good for security.\n   - Performance impact: Negligible.\n\n4. **PIE (Position Independent Executable)**:\n   - Both Nginx and Caddy are now compiled as PIE.\n   - PIE can have a small performance overhead but enhances security through ASLR.\n   - Performance impact: Similar for both, with a small overhead for enhanced security.\n\n5. **Fortify Source**:\n   - Both Nginx and Caddy now have Fortify Source enabled.\n   - Fortify Source adds runtime checks to prevent buffer overflows.\n   - Performance impact: Minimal and similar for both servers.\n\n6. **Compilation Method**:\n   - Caddy was compiled with Clang, which can sometimes produce more optimized code compared to GCC.\n   - The use of `CGO_ENABLED=1` for Caddy allows it to use C libraries, which can impact performance both positively (through optimized C code) and negatively (through CGo overhead).\n\n7. **Fortified Functions**:\n   - Nginx has 4 fortified functions, while Caddy has 2.\n   - This difference is likely due to the different languages and code structures used.\n   - Performance impact: Minimal, with potentially slightly less overhead for Caddy.\n\n8. **RUNPATH**:\n   - Nginx uses RUNPATH, while Caddy does not.\n   - The absence of RUNPATH in Caddy can be seen as a security advantage, reducing the risk of library hijacking.\n   - Performance impact: Negligible.\n\nNginx HTTPS port 443\n\n```\nchecksec --format=json --file=/usr/local/sbin/nginx --extended | jq -r\n{\n  \"/usr/local/sbin/nginx\": {\n    \"relro\": \"full\",\n    \"canary\": \"yes\",\n    \"nx\": \"yes\",\n    \"pie\": \"yes\",\n    \"clangcfi\": \"no\",\n    \"safestack\": \"no\",\n    \"rpath\": \"no\",\n    \"runpath\": \"yes\",\n    \"symbols\": \"no\",\n    \"fortify_source\": \"yes\",\n    \"fortified\": \"4\",\n    \"fortify-able\": \"11\"\n  }\n}\n```\n```\necho -n | openssl s_client -connect domain.com:443 -servername domain.com\nCONNECTED(00000003)\ndepth=0 C = US, ST = California, L = Los Angeles, O = domain.com, OU = domain.com, CN = domain.com\nverify error:num=18:self-signed certificate\nverify return:1\ndepth=0 C = US, ST = California, L = Los Angeles, O = domain.com, OU = domain.com, CN = domain.com\nverify return:1\n---\nCertificate chain\n 0 s:C = US, ST = California, L = Los Angeles, O = domain.com, OU = domain.com, CN = domain.com\n   i:C = US, ST = California, L = Los Angeles, O = domain.com, OU = domain.com, CN = domain.com\n   a:PKEY: id-ecPublicKey, 256 (bit); sigalg: ecdsa-with-SHA256\n   v:NotBefore: Sep  6 03:55:35 2024 GMT; NotAfter: Aug 13 03:55:35 2124 GMT\n---\nServer certificate\n-----BEGIN CERTIFICATE-----\nMIIC9zCCApygAwIBAgIUKHrcCcXPxJXBZnkBoFIPiSWzuHAwCgYIKoZIzj0EAwIw\ndzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFDASBgNVBAcMC0xv\ncyBBbmdlbGVzMRMwEQYDVQQKDApkb21haW4uY29tMRMwEQYDVQQLDApkb21haW4u\nY29tMRMwEQYDVQQDDApkb21haW4uY29tMCAXDTI0MDkwNjAzNTUzNVoYDzIxMjQw\nODEzMDM1NTM1WjB3MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEU\nMBIGA1UEBwwLTG9zIEFuZ2VsZXMxEzARBgNVBAoMCmRvbWFpbi5jb20xEzARBgNV\nBAsMCmRvbWFpbi5jb20xEzARBgNVBAMMCmRvbWFpbi5jb20wWTATBgcqhkjOPQIB\nBggqhkjOPQMBBwNCAARRyp52igUh+rJmG3UbuVg0PZmUPodPsWbex+HrotEyUJh7\n2tBiPjqokOADcR2jInj+kP6Ur8W3gpo8o+3Hx2G5o4IBAjCB/zCBngYDVR0jBIGW\nMIGToXukeTB3MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEUMBIG\nA1UEBwwLTG9zIEFuZ2VsZXMxEzARBgNVBAoMCmRvbWFpbi5jb20xEzARBgNVBAsM\nCmRvbWFpbi5jb20xEzARBgNVBAMMCmRvbWFpbi5jb22CFCh63AnFz8SVwWZ5AaBS\nD4kls7hwMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgTwMCUGA1UdEQQeMByCCmRvbWFp\nbi5jb22CDnd3dy5kb21haW4uY29tMB0GA1UdDgQWBBR6al324gfYcQH7IUnzfvCw\n95hLfjAKBggqhkjOPQQDAgNJADBGAiEA3mmvE/rkJLqK32ZjjHLFOZ+uIPFiXNp2\n+l2TA+5BEQoCIQC4ThTKewmZiuTEu33Aq4pfqTSDQ8mCwmWSXgaIfjRbYA==\n-----END CERTIFICATE-----\nsubject=C = US, ST = California, L = Los Angeles, O = domain.com, OU = domain.com, CN = domain.com\nissuer=C = US, ST = California, L = Los Angeles, O = domain.com, OU = domain.com, CN = domain.com\n---\nNo client certificate CA names sent\nPeer signing digest: SHA256\nPeer signature type: ECDSA\nServer Temp Key: X25519, 253 bits\n---\nSSL handshake has read 1073 bytes and written 394 bytes\nVerification error: self-signed certificate\n---\nNew, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384\nServer public key is 256 bit\nSecure Renegotiation IS NOT supported\nCompression: NONE\nExpansion: NONE\nNo ALPN negotiated\nEarly data was not sent\nVerify return code: 18 (self-signed certificate)\n---\nDONE\n```\n\n```\nh2load -t4 -c200 -n5000 -m100 -H \"Accept-Encoding: gzip\" https://domain.com:443\nstarting benchmark...\nspawning thread #0: 50 total client(s). 1250 total requests\nspawning thread #1: 50 total client(s). 1250 total requests\nspawning thread #2: 50 total client(s). 1250 total requests\nspawning thread #3: 50 total client(s). 1250 total requests\nTLS Protocol: TLSv1.3\nCipher: TLS_AES_256_GCM_SHA384\nServer Temp Key: X25519 253 bits\nApplication protocol: h2\nprogress: 10% done\nprogress: 20% done\nprogress: 30% done\nprogress: 40% done\nprogress: 50% done\nprogress: 60% done\nprogress: 70% done\nprogress: 80% done\nprogress: 90% done\nprogress: 100% done\n\nfinished in 136.36ms, 36668.18 req/s, 79.27MB/s\nrequests: 5000 total, 5000 started, 5000 done, 5000 succeeded, 0 failed, 0 errored, 0 timeout\nstatus codes: 5000 2xx, 0 3xx, 0 4xx, 0 5xx\ntraffic: 10.81MB (11334800) total, 1010.74KB (1035000) headers (space savings 26.86%), 9.73MB (10200000) data\n                     min         max         mean         sd        +/- sd\ntime for request:    11.28ms     96.89ms     43.93ms     22.94ms    54.76%\ntime for connect:     5.48ms     38.78ms     20.52ms      8.88ms    62.50%\ntime to 1st byte:    25.20ms    131.55ms     62.99ms     30.20ms    58.00%\nreq/s           :     184.58      982.30      480.92      236.54    62.50%\n```\n```\nh2load -t4 -c300 -n5000 -m100 -H \"Accept-Encoding: gzip\" https://domain.com:443\nstarting benchmark...\nspawning thread #0: 75 total client(s). 1250 total requests\nspawning thread #1: 75 total client(s). 1250 total requests\nspawning thread #2: 75 total client(s). 1250 total requests\nspawning thread #3: 75 total client(s). 1250 total requests\nTLS Protocol: TLSv1.3\nCipher: TLS_AES_256_GCM_SHA384\nServer Temp Key: X25519 253 bits\nApplication protocol: h2\nprogress: 10% done\nprogress: 20% done\nprogress: 30% done\nprogress: 40% done\nprogress: 50% done\nprogress: 60% done\nprogress: 70% done\nprogress: 80% done\nprogress: 90% done\nprogress: 100% done\n\nfinished in 152.96ms, 32689.35 req/s, 70.70MB/s\nrequests: 5000 total, 5000 started, 5000 done, 5000 succeeded, 0 failed, 0 errored, 0 timeout\nstatus codes: 5000 2xx, 0 3xx, 0 4xx, 0 5xx\ntraffic: 10.81MB (11339700) total, 1010.74KB (1035000) headers (space savings 26.86%), 9.73MB (10200000) data\n                     min         max         mean         sd        +/- sd\ntime for request:     1.49ms     92.72ms     39.64ms     19.19ms    70.26%\ntime for connect:     7.50ms    110.90ms     41.50ms     20.74ms    80.67%\ntime to 1st byte:    38.11ms    144.60ms     80.25ms     29.36ms    62.33%\nreq/s           :     114.24      418.33      230.91       78.24    50.67%\n```\n```\nh2load -t4 -c500 -n10000 -m100 -H \"Accept-Encoding: gzip\" https://domain.com:443\nstarting benchmark...\nspawning thread #0: 125 total client(s). 2500 total requests\nspawning thread #1: 125 total client(s). 2500 total requests\nspawning thread #2: 125 total client(s). 2500 total requests\nspawning thread #3: 125 total client(s). 2500 total requests\nTLS Protocol: TLSv1.3\nCipher: TLS_AES_256_GCM_SHA384\nServer Temp Key: X25519 253 bits\nApplication protocol: h2\nprogress: 10% done\nprogress: 20% done\nprogress: 30% done\nprogress: 40% done\nprogress: 50% done\nprogress: 60% done\nprogress: 70% done\nprogress: 80% done\nprogress: 90% done\nprogress: 100% done\n\nfinished in 271.58ms, 36821.97 req/s, 79.62MB/s\nrequests: 10000 total, 10000 started, 10000 done, 10000 succeeded, 0 failed, 0 errored, 0 timeout\nstatus codes: 10000 2xx, 0 3xx, 0 4xx, 0 5xx\ntraffic: 21.62MB (22674500) total, 1.97MB (2070000) headers (space savings 26.86%), 19.45MB (20400000) data\n                     min         max         mean         sd        +/- sd\ntime for request:     8.44ms    122.85ms     55.67ms     24.41ms    59.69%\ntime for connect:    11.22ms    161.34ms     71.70ms     42.37ms    62.40%\ntime to 1st byte:    46.95ms    254.00ms    126.03ms     56.04ms    56.60%\nreq/s           :      77.79      424.24      196.66      102.71    73.20%\n```\n```\nh2load -t4 -c600 -n50000 -m100 -H \"Accept-Encoding: gzip\" https://domain.com:443\nstarting benchmark...\nspawning thread #0: 150 total client(s). 12500 total requests\nspawning thread #1: 150 total client(s). 12500 total requests\nspawning thread #2: 150 total client(s). 12500 total requests\nspawning thread #3: 150 total client(s). 12500 total requests\nTLS Protocol: TLSv1.3\nCipher: TLS_AES_256_GCM_SHA384\nServer Temp Key: X25519 253 bits\nApplication protocol: h2\nprogress: 10% done\nprogress: 20% done\nprogress: 30% done\nprogress: 40% done\nprogress: 50% done\nprogress: 60% done\nprogress: 70% done\nprogress: 80% done\nprogress: 90% done\nprogress: 100% done\n\nfinished in 1.11s, 44867.03 req/s, 96.94MB/s\nrequests: 50000 total, 50000 started, 50000 done, 50000 succeeded, 0 failed, 0 errored, 0 timeout\nstatus codes: 50000 2xx, 0 3xx, 0 4xx, 0 5xx\ntraffic: 108.03MB (113279400) total, 9.87MB (10350000) headers (space savings 26.86%), 97.27MB (102000000) data\n                     min         max         mean         sd        +/- sd\ntime for request:    29.57ms    787.24ms    329.38ms    189.16ms    63.44%\ntime for connect:    22.73ms    381.14ms    196.48ms    137.78ms    50.83%\ntime to 1st byte:    52.83ms       1.09s    520.12ms    294.40ms    57.83%\nreq/s           :      75.20     1569.15      283.74      313.41    88.50%\n```\n\nCaddy HTTPS port 8443:\n\n```\nchecksec --format=json --file=/usr/local/bin/caddy --extended | jq -r\n{\n  \"/usr/local/bin/caddy\": {\n    \"relro\": \"partial\",\n    \"canary\": \"no\",\n    \"nx\": \"yes\",\n    \"pie\": \"yes\",\n    \"clangcfi\": \"no\",\n    \"safestack\": \"no\",\n    \"rpath\": \"no\",\n    \"runpath\": \"no\",\n    \"symbols\": \"no\",\n    \"fortify_source\": \"yes\",\n    \"fortified\": \"2\",\n    \"fortify-able\": \"2\"\n  }\n}\n```\n```\necho -n | openssl s_client -connect domain.com:8443 -servername domain.com\nCONNECTED(00000003)\ndepth=0 C = US, ST = California, L = Los Angeles, O = domain.com, OU = domain.com, CN = domain.com\nverify error:num=18:self-signed certificate\nverify return:1\ndepth=0 C = US, ST = California, L = Los Angeles, O = domain.com, OU = domain.com, CN = domain.com\nverify return:1\n---\nCertificate chain\n 0 s:C = US, ST = California, L = Los Angeles, O = domain.com, OU = domain.com, CN = domain.com\n   i:C = US, ST = California, L = Los Angeles, O = domain.com, OU = domain.com, CN = domain.com\n   a:PKEY: id-ecPublicKey, 256 (bit); sigalg: ecdsa-with-SHA256\n   v:NotBefore: Sep  6 03:55:35 2024 GMT; NotAfter: Aug 13 03:55:35 2124 GMT\n---\nServer certificate\n-----BEGIN CERTIFICATE-----\nMIIC9zCCApygAwIBAgIUKHrcCcXPxJXBZnkBoFIPiSWzuHAwCgYIKoZIzj0EAwIw\ndzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFDASBgNVBAcMC0xv\ncyBBbmdlbGVzMRMwEQYDVQQKDApkb21haW4uY29tMRMwEQYDVQQLDApkb21haW4u\nY29tMRMwEQYDVQQDDApkb21haW4uY29tMCAXDTI0MDkwNjAzNTUzNVoYDzIxMjQw\nODEzMDM1NTM1WjB3MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEU\nMBIGA1UEBwwLTG9zIEFuZ2VsZXMxEzARBgNVBAoMCmRvbWFpbi5jb20xEzARBgNV\nBAsMCmRvbWFpbi5jb20xEzARBgNVBAMMCmRvbWFpbi5jb20wWTATBgcqhkjOPQIB\nBggqhkjOPQMBBwNCAARRyp52igUh+rJmG3UbuVg0PZmUPodPsWbex+HrotEyUJh7\n2tBiPjqokOADcR2jInj+kP6Ur8W3gpo8o+3Hx2G5o4IBAjCB/zCBngYDVR0jBIGW\nMIGToXukeTB3MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEUMBIG\nA1UEBwwLTG9zIEFuZ2VsZXMxEzARBgNVBAoMCmRvbWFpbi5jb20xEzARBgNVBAsM\nCmRvbWFpbi5jb20xEzARBgNVBAMMCmRvbWFpbi5jb22CFCh63AnFz8SVwWZ5AaBS\nD4kls7hwMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgTwMCUGA1UdEQQeMByCCmRvbWFp\nbi5jb22CDnd3dy5kb21haW4uY29tMB0GA1UdDgQWBBR6al324gfYcQH7IUnzfvCw\n95hLfjAKBggqhkjOPQQDAgNJADBGAiEA3mmvE/rkJLqK32ZjjHLFOZ+uIPFiXNp2\n+l2TA+5BEQoCIQC4ThTKewmZiuTEu33Aq4pfqTSDQ8mCwmWSXgaIfjRbYA==\n-----END CERTIFICATE-----\nsubject=C = US, ST = California, L = Los Angeles, O = domain.com, OU = domain.com, CN = domain.com\nissuer=C = US, ST = California, L = Los Angeles, O = domain.com, OU = domain.com, CN = domain.com\n---\nNo client certificate CA names sent\nPeer signing digest: SHA256\nPeer signature type: ECDSA\nServer Temp Key: X25519, 253 bits\n---\nSSL handshake has read 1117 bytes and written 378 bytes\nVerification error: self-signed certificate\n---\nNew, TLSv1.3, Cipher is TLS_AES_128_GCM_SHA256\nServer public key is 256 bit\nSecure Renegotiation IS NOT supported\nCompression: NONE\nExpansion: NONE\nNo ALPN negotiated\nEarly data was not sent\nVerify return code: 18 (self-signed certificate)\n---\n---\nPost-Handshake New Session Ticket arrived:\nSSL-Session:\n    Protocol  : TLSv1.3\n    Cipher    : TLS_AES_128_GCM_SHA256\n    Session-ID: F1CD1E0388D225EE43208995D7F06D44550CB787CDE4B4A2ED1463FF81CFEEDA\n    Session-ID-ctx: \n    Resumption PSK: 178083B894C8A7A2555E5F32729ED449523CEE54CC9EB0331A50576A0A872024\n    PSK identity: None\n    PSK identity hint: None\n    SRP username: None\n    TLS session ticket lifetime hint: 604800 (seconds)\n    TLS session ticket:\n    0000 - b8 76 cc 4c 6f f6 99 60-2a 15 c4 09 fd 25 ff 95   .v.Lo..`*....%..\n    0010 - 0a 36 d1 99 01 01 9b 98-e1 5a 7d 88 2f c1 9b b8   .6.......Z}./...\n    0020 - 56 24 46 4e 59 48 55 7e-26 de 7f f3 99 15 bf ed   V$FNYHU~\u0026.......\n    0030 - d0 a2 63 cf 54 ee 8f fd-df 0a f1 07 cf 1a 65 41   ..c.T.........eA\n    0040 - 85 42 f7 9c 31 4d 24 b5-15 4c 85 e9 ec 8a 76 fa   .B..1M$..L....v.\n    0050 - f4 51 6b e8 cc f3 43 86-68 1b 3b bf 6d 56 3c c3   .Qk...C.h.;.mV\u003c.\n    0060 - 77 d8 5a 5a bb 75 e9 81-c4                        w.ZZ.u...\n\n    Start Time: 1725735451\n    Timeout   : 7200 (sec)\n    Verify return code: 18 (self-signed certificate)\n    Extended master secret: no\n    Max Early Data: 0\n---\nread R BLOCK\nDONE\n```\n\n```\nh2load -t4 -c200 -n5000 -m100 -H \"Accept-Encoding: gzip\" https://domain.com:8443\nstarting benchmark...\nspawning thread #0: 50 total client(s). 1250 total requests\nspawning thread #1: 50 total client(s). 1250 total requests\nspawning thread #2: 50 total client(s). 1250 total requests\nspawning thread #3: 50 total client(s). 1250 total requests\nTLS Protocol: TLSv1.3\nCipher: TLS_AES_128_GCM_SHA256\nServer Temp Key: X25519 253 bits\nApplication protocol: h2\nprogress: 10% done\nprogress: 20% done\nprogress: 30% done\nprogress: 40% done\nprogress: 50% done\nprogress: 60% done\nprogress: 70% done\nprogress: 80% done\nprogress: 90% done\nprogress: 100% done\n\nfinished in 135.66ms, 36857.66 req/s, 74.87MB/s\nrequests: 5000 total, 5000 started, 5000 done, 5000 succeeded, 0 failed, 0 errored, 0 timeout\nstatus codes: 5000 2xx, 0 3xx, 0 4xx, 0 5xx\ntraffic: 10.16MB (10649322) total, 114.38KB (117122) headers (space savings 93.62%), 9.95MB (10430000) data\n                     min         max         mean         sd        +/- sd\ntime for request:     1.34ms    105.81ms     40.40ms     21.30ms    60.92%\ntime for connect:     8.68ms     71.80ms     37.17ms     14.06ms    63.50%\ntime to 1st byte:    47.90ms    134.20ms     73.23ms     24.47ms    77.50%\nreq/s           :     184.84      519.99      326.53      109.99    61.50%\n```\n```\nh2load -t4 -c300 -n5000 -m100 -H \"Accept-Encoding: gzip\" https://domain.com:8443\nstarting benchmark...\nspawning thread #0: 75 total client(s). 1250 total requests\nspawning thread #1: 75 total client(s). 1250 total requests\nspawning thread #2: 75 total client(s). 1250 total requests\nspawning thread #3: 75 total client(s). 1250 total requests\nTLS Protocol: TLSv1.3\nCipher: TLS_AES_128_GCM_SHA256\nServer Temp Key: X25519 253 bits\nApplication protocol: h2\nprogress: 10% done\nprogress: 20% done\nprogress: 30% done\nprogress: 40% done\nprogress: 50% done\nprogress: 60% done\nprogress: 70% done\nprogress: 80% done\nprogress: 90% done\nprogress: 100% done\n\nfinished in 160.81ms, 31091.82 req/s, 63.32MB/s\nrequests: 5000 total, 5000 started, 5000 done, 5000 succeeded, 0 failed, 0 errored, 0 timeout\nstatus codes: 5000 2xx, 0 3xx, 0 4xx, 0 5xx\ntraffic: 10.18MB (10678024) total, 136.45KB (139724) headers (space savings 92.39%), 9.95MB (10430000) data\n                     min         max         mean         sd        +/- sd\ntime for request:      514us    131.07ms     63.51ms     27.13ms    59.16%\ntime for connect:    10.42ms    114.19ms     41.96ms     13.20ms    74.00%\ntime to 1st byte:    40.55ms    159.51ms     94.68ms     29.95ms    60.67%\nreq/s           :     101.51      393.76      145.70       52.68    89.33%\n```\n```\nh2load -t4 -c500 -n10000 -m100 -H \"Accept-Encoding: gzip\" https://domain.com:8443\nstarting benchmark...\nspawning thread #0: 125 total client(s). 2500 total requests\nspawning thread #1: 125 total client(s). 2500 total requests\nspawning thread #2: 125 total client(s). 2500 total requests\nspawning thread #3: 125 total client(s). 2500 total requests\nTLS Protocol: TLSv1.3\nCipher: TLS_AES_128_GCM_SHA256\nServer Temp Key: X25519 253 bits\nApplication protocol: h2\nprogress: 10% done\nprogress: 20% done\nprogress: 30% done\nprogress: 40% done\nprogress: 50% done\nprogress: 60% done\nprogress: 70% done\nprogress: 80% done\nprogress: 90% done\nprogress: 100% done\n\nfinished in 304.81ms, 32807.21 req/s, 66.72MB/s\nrequests: 10000 total, 10000 started, 10000 done, 10000 succeeded, 0 failed, 0 errored, 0 timeout\nstatus codes: 10000 2xx, 0 3xx, 0 4xx, 0 5xx\ntraffic: 20.34MB (21325000) total, 248.54KB (254500) headers (space savings 93.07%), 19.89MB (20860000) data\n                     min         max         mean         sd        +/- sd\ntime for request:      247us    241.83ms     78.92ms     58.16ms    67.55%\ntime for connect:    17.65ms    270.80ms     90.03ms     47.32ms    79.20%\ntime to 1st byte:    70.36ms    299.01ms    153.86ms     62.06ms    63.60%\nreq/s           :      66.35      283.74      122.00       59.88    80.80%\n```\n```\nh2load -t4 -c600 -n50000 -m100 -H \"Accept-Encoding: gzip\" https://domain.com:8443\nstarting benchmark...\nspawning thread #0: 150 total client(s). 12500 total requests\nspawning thread #1: 150 total client(s). 12500 total requests\nspawning thread #2: 150 total client(s). 12500 total requests\nspawning thread #3: 150 total client(s). 12500 total requests\nTLS Protocol: TLSv1.3\nCipher: TLS_AES_128_GCM_SHA256\nServer Temp Key: X25519 253 bits\nApplication protocol: h2\nprogress: 10% done\nprogress: 20% done\nprogress: 30% done\nprogress: 40% done\nprogress: 50% done\nprogress: 60% done\nprogress: 70% done\nprogress: 80% done\nprogress: 90% done\nprogress: 100% done\n\nfinished in 1.04s, 47899.33 req/s, 96.95MB/s\nrequests: 50000 total, 50000 started, 50000 done, 50000 succeeded, 0 failed, 0 errored, 0 timeout\nstatus codes: 50000 2xx, 0 3xx, 0 4xx, 0 5xx\ntraffic: 101.20MB (106114208) total, 857.04KB (877608) headers (space savings 95.22%), 99.47MB (104300000) data\n                     min         max         mean         sd        +/- sd\ntime for request:      486us    974.05ms    327.67ms    280.40ms    64.15%\ntime for connect:    26.56ms       1.02s    271.04ms    251.16ms    78.83%\ntime to 1st byte:   103.26ms       1.03s    537.79ms    266.34ms    59.67%\nreq/s           :      79.98      810.41      126.96       85.16    98.33%\n```\n\n   Here is an example of a `Caddyfile` configured for forward proxying with HTTPS:\n\n```caddy\n{\n        log {\n                output file /var/log/caddy/caddy_errors.log\n                level ERROR\n        }\n}\n\n:8444 {\n        tls /etc/ssl/certs/your_cert.pem /etc/ssl/private/your_key.pem\n\n        route {\n                forward_proxy {\n                        basic_auth yourusername yourpassword\n                        hide_ip\n                        hide_via\n                        probe_resistance secret_token\n                }\n        }\n\n        log {\n                output file /var/log/caddy/forward_proxy_access_8444.log\n                format json\n        }\n}\n```\n\n   This configuration makes Caddy listen on port 443 and act as a forward proxy. You can secure it with basic authentication and add other options like hiding the client's IP and Via header. You’ll need to provide your own SSL certificate and private key for the `tls` directive.\n\n   If you don’t have SSL certificates, you can use Caddy's automatic Let's Encrypt integration by simply specifying a domain name:\n\n```caddy\n{\n        log {\n                output file /var/log/caddy/caddy_errors.log\n                level ERROR\n        }\n}\n\nyourdomain.com:8444 {\n        tls {\n                dns cloudflare # Optional: Use only if using DNS challenge, otherwise remove this line for HTTP-01 challenge\n        }\n\n        route {\n                forward_proxy {\n                        basic_auth yourusername yourpassword\n                        hide_ip\n                        hide_via\n                        probe_resistance secret_token\n                }\n        }\n\n        log {\n                output file /var/log/caddy/forward_proxy_access_8444.log\n                format json\n        }\n}\n```\n\n   In this case, Caddy will automatically generate and manage the SSL certificate for your domain.\n\n### Step 4: Set Up Caddy as a Systemd Service\n\n1. **Create a Caddy Systemd Service**:\n   Create a systemd service file for Caddy at `/etc/systemd/system/caddy.service`:\n\n   ```bash\n   sudo nano /etc/systemd/system/caddy.service\n   ```\n\n   Add the following content:\n\n   ```ini\n   [Unit]\n   Description=Caddy web server\n   After=network.target\n\n   [Service]\n   User=caddy\n   Group=caddy\n   ExecStart=/usr/local/bin/caddy run --config /etc/caddy/Caddyfile\n   ExecReload=/usr/local/bin/caddy reload --config /etc/caddy/Caddyfile\n   Restart=on-failure\n   LimitNOFILE=1048576\n\n   [Install]\n   WantedBy=multi-user.target\n   ```\n\n2. **Reload Systemd and Start Caddy**:\n   Reload the systemd daemon and start Caddy:\n\n   ```bash\n   sudo systemctl daemon-reload\n   sudo systemctl start caddy\n   sudo systemctl enable caddy\n   sudo systemctl status caddy --no-pager -l\n   sudo journalctl -u caddy.service --no-pager -l\n   ```\n\n3. **Check Caddy Status**:\n   Ensure Caddy is running without issues:\n\n   ```bash\n   sudo systemctl status caddy\n   ```\n   ```bash\n   sudo systemctl status caddy --no-pager -l\n   ● caddy.service - Caddy web server\n        Loaded: loaded (/etc/systemd/system/caddy.service; enabled; preset: disabled)\n        Active: active (running) since Sat 2024-09-07 14:47:29 UTC; 3min 15s ago\n      Main PID: 1902173 (caddy)\n         Tasks: 18 (limit: 48720)\n        Memory: 11.5M\n           CPU: 58ms\n        CGroup: /system.slice/caddy.service\n                └─1902173 /usr/local/bin/caddy run --config /etc/caddy/Caddyfile\n   \n   Sep 07 14:47:29 almalinux9dev1 systemd[1]: Started Caddy web server.\n   Sep 07 14:47:29 almalinux9dev1 caddy[1902173]: {\"level\":\"info\",\"ts\":1725720449.1528602,\"msg\":\"using config from file\",\"file\":\"/etc/caddy/Caddyfile\"}\n   Sep 07 14:47:29 almalinux9dev1 caddy[1902173]: {\"level\":\"info\",\"ts\":1725720449.1533403,\"msg\":\"adapted config to JSON\",\"adapter\":\"caddyfile\"}\n   Sep 07 14:47:29 almalinux9dev1 caddy[1902173]: {\"level\":\"info\",\"ts\":1725720449.1535723,\"msg\":\"redirected default logger\",\"from\":\"stderr\",\"to\":\"/var/log/caddy/caddy_errors.log\"}\n   ```\n\n### Step 5: Test Your Proxy\n\n1. **Testing**: \n   You can now test your Caddy forward proxy by configuring your browser or curl command to use the proxy.\n\n   **Example** for using the proxy with `curl` with non-HTTPS:\n   ```bash\n   curl -x http://yourusername:yourpassword@your_domain.com:8081 http://example.com\n   ```\n\n   **Example** for using the proxy with `curl` with HTTPS:\n   ```bash\n   curl -x https://yourusername:yourpassword@your_domain.com:8444 http://example.com\n   ```\n\n   If the forward proxy is working correctly, you should be able to browse the web through Caddy.\n\n   Example output for non-HTTPS Caddy HTTP forward proxy curl test:\n\n   ```\n   curl -x http://yourusername:yourpassword@192.168.122.60:8081 -Iv https://centminmod.com\n   *   Trying 192.168.122.60:8081...\n   * Connected to 192.168.122.60 (192.168.122.60) port 8081 (#0)\n   * allocate connect buffer!\n   * Establish HTTP proxy tunnel to centminmod.com:443\n   * Proxy auth using Basic with user 'yourusername'\n   \u003e CONNECT centminmod.com:443 HTTP/1.1\n   \u003e Host: centminmod.com:443\n   \u003e Proxy-Authorization: Basic eW91cnVzZXJuYW1lOnlvdXJwYXNzd29yZA==\n   \u003e User-Agent: curl/7.76.1\n   \u003e Proxy-Connection: Keep-Alive\n   \u003e \n   \u003c HTTP/1.1 200 OK\n   HTTP/1.1 200 OK\n   \u003c Server: Caddy\n   Server: Caddy\n   \u003c Content-Length: 0\n   Content-Length: 0\n   * Ignoring Content-Length in CONNECT 200 response\n   \u003c \n\n   * Proxy replied 200 to CONNECT request\n   * CONNECT phase completed!\n   * ALPN, offering h2\n   * ALPN, offering http/1.1\n   *  CAfile: /etc/pki/tls/certs/ca-bundle.crt\n   * TLSv1.0 (OUT), TLS header, Certificate Status (22):\n   * TLSv1.3 (OUT), TLS handshake, Client hello (1):\n   * CONNECT phase completed!\n   * CONNECT phase completed!\n   * TLSv1.2 (IN), TLS header, Certificate Status (22):\n   * TLSv1.3 (IN), TLS handshake, Server hello (2):\n   * TLSv1.2 (IN), TLS header, Finished (20):\n   * TLSv1.2 (IN), TLS header, Unknown (23):\n   * TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):\n   * TLSv1.3 (IN), TLS handshake, Certificate (11):\n   * TLSv1.3 (IN), TLS handshake, CERT verify (15):\n   * TLSv1.3 (IN), TLS handshake, Finished (20):\n   * TLSv1.2 (OUT), TLS header, Finished (20):\n   * TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):\n   * TLSv1.2 (OUT), TLS header, Unknown (23):\n   * TLSv1.3 (OUT), TLS handshake, Finished (20):\n   * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384\n   * ALPN, server accepted to use h2\n   * Server certificate:\n   *  subject: CN=centminmod.com\n   *  start date: Sep  5 22:26:26 2024 GMT\n   *  expire date: Dec  4 22:26:25 2024 GMT\n   *  subjectAltName: host \"centminmod.com\" matched cert's \"centminmod.com\"\n   *  issuer: C=US; O=Let's Encrypt; CN=E5\n   *  SSL certificate verify ok.\n   * Using HTTP2, server supports multi-use\n   * Connection state changed (HTTP/2 confirmed)\n   * Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0\n   * TLSv1.2 (OUT), TLS header, Unknown (23):\n   * TLSv1.2 (OUT), TLS header, Unknown (23):\n   * TLSv1.2 (OUT), TLS header, Unknown (23):\n   * Using Stream ID: 1 (easy handle 0x55e879b41d80)\n   * TLSv1.2 (OUT), TLS header, Unknown (23):\n   \u003e HEAD / HTTP/2\n   \u003e Host: centminmod.com\n   \u003e user-agent: curl/7.76.1\n   \u003e accept: */*\n   \u003e \n   * TLSv1.2 (IN), TLS header, Unknown (23):\n   * TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):\n   * TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):\n   * old SSL session ID is stale, removing\n   * TLSv1.2 (IN), TLS header, Unknown (23):\n   * TLSv1.2 (OUT), TLS header, Unknown (23):\n   * TLSv1.2 (IN), TLS header, Unknown (23):\n   * TLSv1.2 (IN), TLS header, Unknown (23):\n   \u003c HTTP/2 200 \n   HTTP/2 200 \n   \u003c date: Fri, 06 Sep 2024 11:18:54 GMT\n   date: Fri, 06 Sep 2024 11:18:54 GMT\n   \u003c content-type: text/html; charset=utf-8\n   content-type: text/html; charset=utf-8\n   \u003c vary: Accept-Encoding\n   vary: Accept-Encoding\n   \u003c x-powered-by: centminmod\n   x-powered-by: centminmod\n   \u003c expires: Fri, 13 Sep 2024 11:18:54 GMT\n   expires: Fri, 13 Sep 2024 11:18:54 GMT\n   \u003c cache-control: public, max-age=604800\n   cache-control: public, max-age=604800\n   \u003c link: \u003chttps://centminmod.com/\u003e; rel=\"canonical\"\n   link: \u003chttps://centminmod.com/\u003e; rel=\"canonical\"\n   \u003c x-frame-options: SAMEORIGIN\n   x-frame-options: SAMEORIGIN\n   \u003c x-xss-protection: 1; mode=block\n   x-xss-protection: 1; mode=block\n   \u003c x-content-type-options: nosniff\n   x-content-type-options: nosniff\n   \u003c strict-transport-security: max-age=15638400\n   strict-transport-security: max-age=15638400\n   \u003c referrer-policy: strict-origin-when-cross-origin\n   referrer-policy: strict-origin-when-cross-origin\n   \u003c permissions-policy: interest-cohort=(), accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()\n   permissions-policy: interest-cohort=(), accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()\n   \u003c cwnd: 19\n   cwnd: 19\n   \u003c crtt: 76000\n   crtt: 76000\n   \u003c crqt: 0.000\n   crqt: 0.000\n   \u003c last-modified: Fri, 09 Aug 2024 09:15:03 GMT\n   last-modified: Fri, 09 Aug 2024 09:15:03 GMT\n   \u003c cf-cache-status: HIT\n   cf-cache-status: HIT\n   \u003c age: 2426623\n   age: 2426623\n    \u003c nel: {\"success_fraction\":0.01,\"report_to\":\"cf-nel\",\"max_age\":604800}\n   nel: {\"success_fraction\":0.01,\"report_to\":\"cf-nel\",\"max_age\":604800}\n   \u003c server: cloudflare\n   server: cloudflare\n   \u003c cf-ray: 8bee0f9f0d74840a-LAX\n   cf-ray: 8bee0f9f0d74840a-LAX\n   \u003c alt-svc: h3=\":443\"; ma=86400\n   alt-svc: h3=\":443\"; ma=86400\n\n   \u003c \n   * Connection #0 to host 192.168.122.60 left intact\n   ```\n\n   Inspecting log at `/var/log/caddy/forward_proxy_access_8081.log`:\n\n   ```bash\n   tail -10 /var/log/caddy/forward_proxy_access_8081.log | jq -c | tail -1 | jq -r\n   {\n     \"level\": \"info\",\n     \"ts\": 1725621534.613994,\n     \"logger\": \"http.log.access.log0\",\n     \"msg\": \"handled request\",\n     \"request\": {\n       \"remote_ip\": \"192.168.122.60\",\n       \"remote_port\": \"18080\",\n       \"client_ip\": \"192.168.122.60\",\n       \"proto\": \"HTTP/1.1\",\n       \"method\": \"CONNECT\",\n       \"host\": \"centminmod.com:443\",\n       \"uri\": \"centminmod.com:443\",\n       \"headers\": {\n         \"Proxy-Connection\": [\n           \"Keep-Alive\"\n         ],\n         \"Proxy-Authorization\": [\n           \"REDACTED\"\n         ],\n         \"User-Agent\": [\n           \"curl/7.76.1\"\n         ]\n       }\n     },\n     \"bytes_read\": 848,\n     \"user_id\": \"yourusername\",\n     \"duration\": 0.087575974,\n     \"size\": 5427,\n     \"status\": 0,\n     \"resp_headers\": {\n       \"Server\": [\n         \"Caddy\"\n       ]\n     }\n   }\n   ```\n\n2. **Browser Configuration**:\n   - Set the Caddy server as your HTTP/HTTPS proxy in your browser settings.\n   - Use `yourdomain.com:8444` for HTTPS proxy or `yourdomain.com:8081` for non-HTTPS proxy, and authenticate using the credentials you set up in the `basic_auth` directive.\n\n### Optional: Load Balancing Across Multiple Caddy Instances\n\nIf you want to distribute traffic across multiple Caddy instances, you can set up a load balancer like HAProxy to distribute requests. Here’s a basic example of an HAProxy config that balances across multiple Caddy proxies:\n\n```haproxy\nfrontend http-in\n    bind *:8080\n    mode tcp\n    default_backend caddy-backend\n    # Optionally set timeouts\n    timeout client 300s\n\nbackend caddy-backend\n    mode tcp\n    balance roundrobin\n    server caddy1 192.168.1.10:8081 check\n    server caddy2 192.168.1.11:8081 check\n    server caddy3 192.168.1.12:8081 check\n    # Optionally set timeouts\n    timeout server 300s\n```\n\nOr via Caddy load balancing:\n\n```caddy\n{\n    log {\n        output file /var/log/caddy/caddy_errors.log\n        level ERROR\n    }\n}\n\n:8080 {\n    reverse_proxy {\n        to 192.168.1.10:8081 192.168.1.11:8081 192.168.1.12:8081\n        lb_policy round_robin  # Round robin load balancing policy\n\n        # Optionally set timeouts\n        transport http {\n            dial_timeout 5s\n            response_header_timeout 300s\n            keepalive 300s\n        }\n\n        # Active health checks (optional)\n        health_uri /health  # Health check endpoint\n        health_interval 30s # Frequency of health checks\n        health_timeout 5s   # Timeout for health checks\n        health_status 200   # Expected status code for healthy backends\n    }\n    \n    log {\n        output file /var/log/caddy/forward_proxy_access_8080.log\n        format json\n    }\n}\n```\n\nCombined with Caddy HTTP forward proxy:\n\n```caddy\n{\n    log {\n        output file /var/log/caddy/caddy_errors.log\n        level ERROR\n    }\n}\n\n# Forward Proxy Configuration on Port 8081\n:8081 {\n    route {\n        forward_proxy {\n            basic_auth yourusername yourpassword\n            hide_ip\n            hide_via\n            probe_resistance secret_token\n        }\n    }\n    log {\n        output file /var/log/caddy/forward_proxy_access_8081.log\n        format json\n    }\n}\n\n# Load Balancer Configuration on Port 8080\n:8080 {\n    reverse_proxy {\n        to 192.168.1.10:8081 192.168.1.11:8081 192.168.1.12:8081  # Backend servers\n        lb_policy round_robin  # Round-robin load balancing policy\n\n        # Optionally set timeouts\n        transport http {\n            dial_timeout 5s\n            response_header_timeout 300s\n            keepalive 300s\n        }\n\n        # Active health checks (optional)\n        health_uri /health  # Health check endpoint\n        health_interval 30s # Frequency of health checks\n        health_timeout 5s   # Timeout for health checks\n        health_status 200   # Expected status code for healthy backends\n    }\n    \n    log {\n        output file /var/log/caddy/forward_proxy_access_8080.log\n        format json\n    }\n}\n```\n\n- **Forward Proxy on Port 8081**: serving the forward proxy as it was before.\n- **Load Balancer on Port 8080**:\n  - **`reverse_proxy`**: This directive is used to define the load balancing behavior.\n  - **Backends**: The `to` directive specifies the backend servers (`192.168.1.10`, `192.168.1.11`, `192.168.1.12`) running on port `8081`.\n  - **Load Balancing Policy**: The `lb_policy round_robin` ensures that traffic is evenly distributed across backends.\n  - **Health Checks**: Optionally, health checks are enabled via `health_uri`, ensuring that only healthy backends are used.\n  - **Logging**: Access logs for the load balancer are saved to `/var/log/caddy/forward_proxy_access_8080.log` in JSON format.\n\nThis configuration maintains your existing forward proxy on port `8081` while adding a new load balancer on port `8080` that distributes traffic across multiple backend servers.\n\n### Conclusion\n\nBy following these steps, you will have built Caddy with the forward proxy plugin on AlmaLinux 8/9, configured it for HTTP forwarding, and set it up as a systemd service. If you want to scale the solution, you can introduce load balancing across multiple Caddy instances using HAProxy or another load balancer.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcentminmod%2Fcaddy-forward-proxy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcentminmod%2Fcaddy-forward-proxy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcentminmod%2Fcaddy-forward-proxy/lists"}