From 8f9bba469a5210dd77387d39df20c59a170941ef Mon Sep 17 00:00:00 2001 From: AlexV Date: Sat, 10 Oct 2026 11:04:50 +0700 Subject: [PATCH 1/8] feat: replace Nginx with Caddy as the default HTTP server and reverse proxy for runtime Signed-off-by: AlexV --- CONTRIBUTING.md | 16 +++---- Dockerfile | 9 ++-- build/base/runtime/apko.yaml | 4 +- docker-compose.yml | 2 + docs/backend/README.md | 2 +- docs/frontend/README.md | 2 +- init/container-entrypoint.sh | 23 ++++----- init/pebble/001-cattr.yaml | 9 ++-- runtime/caddy/Caddyfile | 40 ++++++++++++++++ runtime/nginx/conf.d/app.conf | 72 ---------------------------- runtime/nginx/nginx.conf | 89 ----------------------------------- 11 files changed, 77 insertions(+), 191 deletions(-) create mode 100644 runtime/caddy/Caddyfile delete mode 100644 runtime/nginx/conf.d/app.conf delete mode 100644 runtime/nginx/nginx.conf diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9b3b4748..7ee7bf0d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -271,17 +271,17 @@ The repository contains both the server-side application and the web frontend. | `resources/frontend/` | Vue frontend application | | `modules/` | Backend modules | | `tests/` | Backend unit and feature tests | -| `runtime/` | Runtime configuration for nginx, PHP, and cron | +| `runtime/` | Runtime configuration for Caddy, PHP, and cron | | `init/` | Container entrypoint and Pebble service configuration | | `build/base/` | apko definitions for runtime and builder images | | `build/packages/` | Melange package definitions | | `.github/workflows/` | GitHub Actions workflows | | `.helm/` | Helm deployment files | -The production application is distributed as a single container containing the backend, compiled frontend, nginx, Laravel Octane, queue worker, Laravel Reverb, and scheduled jobs. +The production application is distributed as a single container containing the backend, compiled frontend, Caddy, Laravel Octane, queue worker, Laravel Reverb, and scheduled jobs. The production build is Wolfi-based: the builder uses Node.js 24 with pnpm 12.5.1, -and the runtime uses PHP 8.3, nginx, Pebble, Laravel Octane, and Laravel Reverb. +and the runtime uses PHP 8.3, Caddy, Pebble, Laravel Octane, and Laravel Reverb. ## Development requirements @@ -431,8 +431,8 @@ php artisan schedule:work The local development server does not reproduce the complete production runtime. -Production uses nginx on port 8080 and Laravel Octane with Swoole. Pebble supervises -nginx, Octane, Reverb, the queue worker, and scheduled jobs in the rootless runtime. +Production uses Caddy on port 8080 and Laravel Octane with Swoole. Pebble supervises +Caddy, Octane, Reverb, the queue worker, and scheduled jobs in the rootless runtime. ## Frontend development @@ -717,7 +717,7 @@ podman build -t cattr-server . The final image runs as UID/GID `10000`, uses `/opt/cattr/app` as its working directory, and stores mutable Laravel data under `/opt/cattr/app/storage`. -The image exposes nginx on `8080`, Reverb on `8081`, and Octane on `8090`. +The image exposes Caddy on `8080`, Reverb on `8081`, and Octane on `8090`. The base images can be overridden with: @@ -737,7 +737,7 @@ The production container uses Pebble to manage its services and initialization s The runtime includes: ```text -nginx +Caddy Laravel Octane Laravel Queue Laravel Reverb @@ -768,7 +768,7 @@ build/base/builder/apko.yaml ``` The Wolfi builder image contains Composer, PHP 8.3 builder extensions, Node.js 24, -Git, OpenSSH, and Corepack. The runtime image contains PHP 8.3, nginx, Pebble, +Git, OpenSSH, and Corepack. The runtime image contains PHP 8.3, Caddy, Pebble, supercronic, and the packages needed to run Cattr. Corepack is packaged with Melange: diff --git a/Dockerfile b/Dockerfile index 640cdd0a..8ab66116 100644 --- a/Dockerfile +++ b/Dockerfile @@ -91,11 +91,10 @@ RUN chown -R 10000:10000 /opt/cattr/app/bootstrap/cache /opt/cattr/app/storage USER 10000:10000 # Cattr-specific configuration lives under /opt/cattr/etc. Program-specific -# system configuration remains in the conventional /etc/php and /etc/nginx. +# system configuration remains in the conventional /etc/php and /etc/caddy. COPY --chown=0:0 --chmod=0644 runtime/supercronic/crontab /opt/cattr/crontab COPY --chown=0:0 --chmod=0644 runtime/php/conf.d/99-cattr.ini /etc/php/conf.d/99-cattr.ini -COPY --chown=0:0 --chmod=0644 runtime/nginx/nginx.conf /etc/nginx/nginx.conf -COPY --chown=0:0 --chmod=0644 runtime/nginx/conf.d/app.conf /etc/nginx/conf.d/app.conf +COPY --chown=0:0 --chmod=0644 runtime/caddy/Caddyfile /etc/caddy/Caddyfile # Pebble needs a writable per-user state directory; the base image creates it # as UID/GID 10000. The layer itself is application data copied at image build. @@ -108,8 +107,8 @@ USER 10000:10000 VOLUME ["/opt/cattr/app/storage"] -# 8080 nginx, 8081 Reverb, 8090 Octane. The latter two matter when a role is -# deployed without nginx in front of it. +# 8080 Caddy, 8081 Reverb, 8090 Octane. The latter two matter when a role is +# deployed without Caddy in front of it. EXPOSE 8080 8081 8090 STOPSIGNAL SIGTERM diff --git a/build/base/runtime/apko.yaml b/build/base/runtime/apko.yaml index b59dcc6a..bb7e23a8 100644 --- a/build/base/runtime/apko.yaml +++ b/build/base/runtime/apko.yaml @@ -12,13 +12,15 @@ contents: - pebble - supercronic=0.2.49-r3 - - nginx-mainline=1.31.6-r0 + - caddy # Local metapackage pins PHP and all required extensions. - php83-local environment: HOME: /opt/cattr + XDG_CONFIG_HOME: /tmp/caddy/config + XDG_DATA_HOME: /tmp/caddy/data PATH: /usr/sbin:/sbin:/bin:/usr/bin paths: diff --git a/docker-compose.yml b/docker-compose.yml index 63c320cf..297b918e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,6 +8,8 @@ services: condition: service_healthy volumes: - backend_storage:/opt/cattr/app/storage + env_file: + - .env ports: - "8080:8080" diff --git a/docs/backend/README.md b/docs/backend/README.md index 4946c7cc..4586847e 100644 --- a/docs/backend/README.md +++ b/docs/backend/README.md @@ -20,4 +20,4 @@ Serve for production pnpm --filter cattr-docs-backend start ``` -> Notice: you should proxy nginx to localhost:4000 +> Notice: you can put a reverse proxy in front of localhost:4000. diff --git a/docs/frontend/README.md b/docs/frontend/README.md index 6e935313..dfbcab4d 100644 --- a/docs/frontend/README.md +++ b/docs/frontend/README.md @@ -20,4 +20,4 @@ Serve for production pnpm --filter cattr-docs-frontend start ``` -> Notice: you should proxy nginx to localhost:4000 +> Notice: you can put a reverse proxy in front of localhost:4000. diff --git a/init/container-entrypoint.sh b/init/container-entrypoint.sh index 0aa03f78..beffca50 100755 --- a/init/container-entrypoint.sh +++ b/init/container-entrypoint.sh @@ -72,11 +72,8 @@ prepare_directories() { /opt/cattr/app/storage/framework/sessions \ /opt/cattr/app/storage/framework/views \ /opt/cattr/app/storage/logs \ - /tmp/nginx/client_body \ - /tmp/nginx/fastcgi \ - /tmp/nginx/proxy \ - /tmp/nginx/scgi \ - /tmp/nginx/uwsgi + /tmp/caddy/data \ + /tmp/caddy/config } cache_configuration() { @@ -131,23 +128,27 @@ for role in "$@"; do # Preserve the historical single-container behaviour. run_migrations=1 run_provision=1 - add_service nginx + add_service caddy add_service reverb add_service queue add_service scheduler ;; web) - # nginx requires app in the Pebble plan. Reverb stays in the web - # pod because the default nginx config proxies WebSockets to + # Caddy requires app in the Pebble plan. Reverb stays in the web + # pod because the default Caddy config proxies WebSockets to # 127.0.0.1:8081. - add_service nginx + add_service caddy add_service reverb ;; app) add_service app ;; nginx) - add_service nginx + # Keep the historical role name as an alias for existing deploys. + add_service caddy + ;; + caddy) + add_service caddy ;; queue) add_service queue @@ -170,7 +171,7 @@ for role in "$@"; do ;; *) log "ERROR: unknown Cattr role: ${role}" - log "Supported roles: all web app nginx queue reverb scheduler migrate provision setup exec" + log "Supported roles: all web app caddy queue reverb scheduler migrate provision setup exec (nginx is a legacy alias for caddy)" exit 64 ;; esac diff --git a/init/pebble/001-cattr.yaml b/init/pebble/001-cattr.yaml index 79976b67..7376e492 100644 --- a/init/pebble/001-cattr.yaml +++ b/init/pebble/001-cattr.yaml @@ -16,10 +16,13 @@ services: on-failure: shutdown kill-delay: 30s - nginx: + caddy: override: replace - summary: Cattr HTTP reverse proxy and static file server - command: nginx -e stderr -g "daemon off;" + summary: Cattr Caddy reverse proxy and static file server + command: caddy run --config /etc/caddy/Caddyfile --adapter caddyfile + environment: + XDG_CONFIG_HOME: /tmp/caddy/config + XDG_DATA_HOME: /tmp/caddy/data startup: disabled requires: - app diff --git a/runtime/caddy/Caddyfile b/runtime/caddy/Caddyfile new file mode 100644 index 00000000..cb05d69d --- /dev/null +++ b/runtime/caddy/Caddyfile @@ -0,0 +1,40 @@ +{ + admin off +} + +:8080 { + root * /opt/cattr/app/public + encode gzip + log { + output stdout + } + request_body { + max_size 512MiB + } + + @websocket { + header Connection *Upgrade* + header Upgrade websocket + } + @php path *.php + @static { + file {path} + not path *.php + } + + route { + reverse_proxy @websocket 127.0.0.1:8081 + + reverse_proxy @php 127.0.0.1:8090 { + header_up SERVER_PORT {server_port} + header_up REMOTE_ADDR {remote_host} + } + + file_server @static + + reverse_proxy 127.0.0.1:8090 { + header_up SERVER_PORT {server_port} + header_up REMOTE_ADDR {remote_host} + } + } +} diff --git a/runtime/nginx/conf.d/app.conf b/runtime/nginx/conf.d/app.conf deleted file mode 100644 index d994701e..00000000 --- a/runtime/nginx/conf.d/app.conf +++ /dev/null @@ -1,72 +0,0 @@ -map $http_upgrade $type { - default "web"; - websocket "ws"; -} - -server { - # Port 80 would require CAP_NET_BIND_SERVICE. Keep the runtime capability-free. - listen 8080 default_server; - server_name _; - - root /opt/cattr/app/public; - index index.php; - - location / { - try_files /i-should-not-exist @$type; - } - - location @web { - try_files $uri $uri/ @octane; - } - - location = /favicon.ico { - access_log off; - log_not_found off; - } - - location = /robots.txt { - access_log off; - log_not_found off; - } - - location @ws { - proxy_set_header Host $http_host; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - proxy_cache_bypass $http_upgrade; - proxy_http_version 1.1; - proxy_redirect off; - - proxy_pass http://127.0.0.1:8081; - } - - location @octane { - proxy_send_timeout 300; - proxy_read_timeout 300; - - client_max_body_size 512M; - client_body_temp_path /tmp/nginx/client_body; - - proxy_set_header Host $http_host; - proxy_set_header SERVER_PORT $server_port; - proxy_set_header REMOTE_ADDR $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - - proxy_pass http://127.0.0.1:8090$uri?$query_string; - } - - location ~ \.php$ { - proxy_send_timeout 300; - proxy_read_timeout 300; - - client_max_body_size 512M; - client_body_temp_path /tmp/nginx/client_body; - - proxy_set_header Host $http_host; - proxy_set_header SERVER_PORT $server_port; - proxy_set_header REMOTE_ADDR $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - - proxy_pass http://127.0.0.1:8090$uri?$query_string; - } -} diff --git a/runtime/nginx/nginx.conf b/runtime/nginx/nginx.conf deleted file mode 100644 index 57f1aee7..00000000 --- a/runtime/nginx/nginx.conf +++ /dev/null @@ -1,89 +0,0 @@ -worker_processes auto; -pcre_jit on; - -# Rootless runtime: keep all mutable nginx state out of /run and /var/log. -pid /tmp/nginx.pid; -error_log /dev/stderr warn; - -include /etc/nginx/modules/*.conf; - -worker_rlimit_nofile 64000; - -events { - worker_connections 8192; - multi_accept on; - use epoll; -} - -http { - sendfile on; - tcp_nopush on; - tcp_nodelay on; - - keepalive_timeout 65; - keepalive_requests 2000; - reset_timedout_connection on; - - types_hash_max_size 2048; - client_max_body_size 512m; - - proxy_buffer_size 128k; - proxy_buffers 16 128k; - proxy_busy_buffers_size 128k; - - server_names_hash_bucket_size 64; - server_name_in_redirect off; - server_tokens off; - - include /etc/nginx/mime.types; - default_type application/octet-stream; - - # All temporary files must remain writable by UID/GID 10000. - client_body_temp_path /tmp/nginx/client_body; - proxy_temp_path /tmp/nginx/proxy; - fastcgi_temp_path /tmp/nginx/fastcgi; - uwsgi_temp_path /tmp/nginx/uwsgi; - scgi_temp_path /tmp/nginx/scgi; - - log_format main '$remote_addr - $remote_user [$time_local] "$request" ' - '$status $body_bytes_sent "$http_referer" ' - '"$http_user_agent" "$http_x_forwarded_for"'; - - access_log /dev/stdout main; - - gzip on; - gzip_disable "msie6"; - gzip_min_length 20; - gzip_vary on; - gzip_proxied any; - gzip_comp_level 6; - gzip_buffers 16 8k; - gzip_http_version 1.1; - gzip_types text/css text/x-component application/x-javascript application/javascript - text/javascript text/x-js text/richtext image/svg+xml text/plain text/xsd - text/xsl text/xml image/bmp application/java application/msword - application/vnd.ms-fontobject application/x-msdownload image/x-icon image/webp - application/json application/vnd.ms-access application/vnd.ms-project - application/x-font-otf application/vnd.ms-opentype - application/vnd.oasis.opendocument.database - application/vnd.oasis.opendocument.chart - application/vnd.oasis.opendocument.formula - application/vnd.oasis.opendocument.graphics - application/vnd.oasis.opendocument.spreadsheet - application/vnd.oasis.opendocument.text audio/ogg application/pdf - application/vnd.ms-powerpoint application/x-shockwave-flash image/tiff - application/x-font-ttf audio/wav application/vnd.ms-write application/font-woff - application/font-woff2 application/vnd.ms-excel; - - open_file_cache max=200000 inactive=3600s; - open_file_cache_valid 3600s; - open_file_cache_min_uses 2; - open_file_cache_errors off; - - map $http_upgrade $connection_upgrade { - default upgrade; - '' close; - } - - include /etc/nginx/conf.d/app.conf; -} From 074643fae4f2470759a86c047078095bce902f6b Mon Sep 17 00:00:00 2001 From: AlexV Date: Sat, 10 Oct 2026 11:27:30 +0700 Subject: [PATCH 2/8] feat: remove "Server" and "Via" headers from Caddy HTTP responses for improved security Signed-off-by: AlexV --- runtime/caddy/Caddyfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/runtime/caddy/Caddyfile b/runtime/caddy/Caddyfile index cb05d69d..3206458f 100644 --- a/runtime/caddy/Caddyfile +++ b/runtime/caddy/Caddyfile @@ -5,6 +5,10 @@ :8080 { root * /opt/cattr/app/public encode gzip + header { + -Server + -Via + } log { output stdout } From 24c603839138d69010ee628e66729ff85efece8d Mon Sep 17 00:00:00 2001 From: AlexV Date: Sat, 10 Oct 2026 12:04:59 +0700 Subject: [PATCH 3/8] chore: disable APP_DEBUG in .env.example for production readiness Signed-off-by: AlexV --- .env.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env.example b/.env.example index cc6e80cc..dd5b6f6f 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,6 @@ APP_KEY= # When true, request_content in error logs is NOT encrypted -APP_DEBUG=true +APP_DEBUG=false APP_URL=http://127.0.0.1:8000 REQUEST_SIGNATURE=_CHANGE_ME_ From dbd90c66c4b2726a4eec70a8b12043274141ff3e Mon Sep 17 00:00:00 2001 From: AlexV Date: Sat, 10 Oct 2026 12:07:58 +0700 Subject: [PATCH 4/8] fix: increase read and write timeouts for PHP reverse proxy in Caddy configuration Signed-off-by: AlexV --- runtime/caddy/Caddyfile | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/runtime/caddy/Caddyfile b/runtime/caddy/Caddyfile index 3206458f..0639fb03 100644 --- a/runtime/caddy/Caddyfile +++ b/runtime/caddy/Caddyfile @@ -30,6 +30,10 @@ reverse_proxy @websocket 127.0.0.1:8081 reverse_proxy @php 127.0.0.1:8090 { + transport http { + read_timeout 300s + write_timeout 300s + } header_up SERVER_PORT {server_port} header_up REMOTE_ADDR {remote_host} } @@ -37,6 +41,10 @@ file_server @static reverse_proxy 127.0.0.1:8090 { + transport http { + read_timeout 300s + write_timeout 300s + } header_up SERVER_PORT {server_port} header_up REMOTE_ADDR {remote_host} } From 75abd0e5bad7845127910b5bb055ba7d2928f90e Mon Sep 17 00:00:00 2001 From: AlexV Date: Sat, 10 Oct 2026 12:22:31 +0700 Subject: [PATCH 5/8] feat: enhance Caddy configuration with stricter security headers and flexible header matching rules, update `.env` handling instructions in `docker-compose.yml` Signed-off-by: AlexV --- docker-compose.yml | 2 + runtime/caddy/Caddyfile | 86 +++++++++++++++++++++-------------------- 2 files changed, 46 insertions(+), 42 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 297b918e..e2e7b9c9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,7 @@ version: '3.9' +# Before starting Compose, copy the example environment file: +# cp .env.example .env services: app: build: . diff --git a/runtime/caddy/Caddyfile b/runtime/caddy/Caddyfile index 0639fb03..68a8511a 100644 --- a/runtime/caddy/Caddyfile +++ b/runtime/caddy/Caddyfile @@ -1,52 +1,54 @@ { - admin off + admin off } :8080 { - root * /opt/cattr/app/public - encode gzip - header { - -Server - -Via - } - log { - output stdout - } - request_body { - max_size 512MiB - } + root * /opt/cattr/app/public + encode gzip + header { + -Server + -Via + X-Frame-Options SAMEORIGIN + } + log { + output stdout + } + request_body { + max_size 512MiB + } - @websocket { - header Connection *Upgrade* - header Upgrade websocket - } - @php path *.php - @static { - file {path} - not path *.php - } + @websocket { + header_regexp Connection (?i).*Upgrade.* + header_regexp Upgrade (?i)^websocket$ + } + @php path *.php + @static { + file {path} + expression {file_match.type} == "file" + not path *.php + } - route { - reverse_proxy @websocket 127.0.0.1:8081 + route { + reverse_proxy @websocket 127.0.0.1:8081 - reverse_proxy @php 127.0.0.1:8090 { - transport http { - read_timeout 300s - write_timeout 300s - } - header_up SERVER_PORT {server_port} - header_up REMOTE_ADDR {remote_host} - } + reverse_proxy @php 127.0.0.1:8090 { + transport http { + read_timeout 300s + write_timeout 300s + } + header_up SERVER_PORT 8080 + header_up REMOTE_ADDR {remote_host} + } - file_server @static + file_server @static - reverse_proxy 127.0.0.1:8090 { - transport http { - read_timeout 300s - write_timeout 300s - } - header_up SERVER_PORT {server_port} - header_up REMOTE_ADDR {remote_host} - } - } + reverse_proxy 127.0.0.1:8090 { + transport http { + read_timeout 300s + write_timeout 300s + } + header_up SERVER_PORT 8080 + header_up REMOTE_ADDR {remote_host} + } + } } From e92ce1ce7d156696846b5c09c65b15a48dc3614a Mon Sep 17 00:00:00 2001 From: AlexV Date: Sat, 10 Oct 2026 12:32:51 +0700 Subject: [PATCH 6/8] feat: add X-Content-Type-Options header to Caddy configuration for enhanced security Signed-off-by: AlexV --- runtime/caddy/Caddyfile | 1 + 1 file changed, 1 insertion(+) diff --git a/runtime/caddy/Caddyfile b/runtime/caddy/Caddyfile index 68a8511a..002dbc23 100644 --- a/runtime/caddy/Caddyfile +++ b/runtime/caddy/Caddyfile @@ -9,6 +9,7 @@ -Server -Via X-Frame-Options SAMEORIGIN + X-Content-Type-Options nosniff } log { output stdout From 34ddc9c9136e4dbeef2a5245e89cc99af65d9031 Mon Sep 17 00:00:00 2001 From: AlexV Date: Sat, 10 Oct 2026 12:54:32 +0700 Subject: [PATCH 7/8] feat: pin Caddy version, enhance configuration with stricter proxy settings and improved file matching rules Signed-off-by: AlexV --- build/base/runtime/apko.yaml | 2 +- runtime/caddy/Caddyfile | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/build/base/runtime/apko.yaml b/build/base/runtime/apko.yaml index bb7e23a8..fefd845a 100644 --- a/build/base/runtime/apko.yaml +++ b/build/base/runtime/apko.yaml @@ -12,7 +12,7 @@ contents: - pebble - supercronic=0.2.49-r3 - - caddy + - caddy=2.11.7-r3 # Local metapackage pins PHP and all required extensions. - php83-local diff --git a/runtime/caddy/Caddyfile b/runtime/caddy/Caddyfile index 002dbc23..7ddf7da1 100644 --- a/runtime/caddy/Caddyfile +++ b/runtime/caddy/Caddyfile @@ -1,5 +1,9 @@ { admin off + servers { + trusted_proxies static private_ranges + trusted_proxies_strict + } } :8080 { @@ -24,8 +28,7 @@ } @php path *.php @static { - file {path} - expression {file_match.type} == "file" + expression `file('\{path}') && {file_match.type} == "file"` not path *.php } @@ -38,7 +41,7 @@ write_timeout 300s } header_up SERVER_PORT 8080 - header_up REMOTE_ADDR {remote_host} + header_up REMOTE_ADDR {client_ip} } file_server @static @@ -49,7 +52,7 @@ write_timeout 300s } header_up SERVER_PORT 8080 - header_up REMOTE_ADDR {remote_host} + header_up REMOTE_ADDR {client_ip} } } } From b7615ebef25e586617ccffafa2e83ad4b1047cfa Mon Sep 17 00:00:00 2001 From: AlexV Date: Sat, 10 Oct 2026 13:28:07 +0700 Subject: [PATCH 8/8] chore: update APP_URL port in .env.example for local development alignment Signed-off-by: AlexV --- .env.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env.example b/.env.example index dd5b6f6f..a408b890 100644 --- a/.env.example +++ b/.env.example @@ -1,7 +1,7 @@ APP_KEY= # When true, request_content in error logs is NOT encrypted APP_DEBUG=false -APP_URL=http://127.0.0.1:8000 +APP_URL=http://127.0.0.1:8080 REQUEST_SIGNATURE=_CHANGE_ME_