



























项目结构:
root@iStoreOS:/ssd/openwrt-compose/network/example-nginx# tree -L 2 . ├── conf.d │ └── example-8820.conf ├── html │ └── www.example.com ├── example_cached.sh ├── logs │ ├── access.log │ ├── error.log │ ├── example.access.log │ ├── example.error.log │ └── example_cached.log ├── nginx.conf └── start.sh
example-8820.conf:
# 在 http 块中定义日志格式,增加 $proxy_target 字段 log_format proxy_detailed '$remote_addr - $remote_user [$time_local] ' '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent" ' 'proxied=$proxied ' 'proxy_target="$proxy_target"'; # 新增字段 server { listen 8820 ssl; listen [::]:8820 ssl; server_name lfk.java-jar.fun; client_max_body_size 0; ssl_certificate /etc/uhttpd.crt; ssl_certificate_key /etc/uhttpd.key; error_page 497 https://$host:$server_port$request_uri; # 使用自定义日志格式 access_log /var/log/nginx/example.access.log proxy_detailed; access_log /dev/stdout proxy_detailed; error_log /var/log/nginx/example.error.log error; # 生产环境可改为 warn error_log /dev/stderr error; root "/html/www.example.com"; index index.html index.htm; # resolver 223.5.5.5 223.6.6.6 valid=300s; # resolver_timeout 5s; location / { try_files $uri $uri/ =404; sub_filter_types text/html text/css application/javascript; sub_filter 'https://www.example.com/' '/'; sub_filter '<div class="head-lang"' '<div style="display:none;" class="head-lang"'; sub_filter_once off; } location /uploads/ { set $proxied 0; set $proxy_target ""; try_files $uri @proxy_uploads; } location @proxy_uploads { set $proxied 1; # 构造实际代理的目标 URL(包含原始请求 URI 和查询参数) set $proxy_target "https://www.example.com$request_uri"; proxy_pass https://www.example.com; proxy_set_header Host www.example.com; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_http_version 1.1; proxy_ssl_server_name on; proxy_ssl_protocols TLSv1.2 TLSv1.3; proxy_ssl_verify off; # 调试用,正式环境建议 on proxy_connect_timeout 10s; proxy_send_timeout 10s; proxy_read_timeout 30s; proxy_cache_valid 200 302 1h; proxy_cache_valid 404 1m; } error_page 404 /404.html; location = /404.html { internal; } }
example_cached.sh:
#!/bin/bash # 配置 LOG_FILE="/var/log/nginx/example.access.log" LOCAL_ROOT="/html/www.example.com" ERROR_LOG="/var/log/nginx/example_cached_error.log" # 确保日志文件存在 touch "$LOG_FILE" # 函数:从 proxy_target 字段提取完整 URL extract_url() { # 格式:... proxy_target="https://www.example.com/xxxx?params" # 使用 grep -oP 提取双引号内的内容(需要 GNU grep 支持 -P) echo "$1" | grep -oP 'proxy_target="\K[^"]+' | head -1 } # 函数:从 URL 中提取 URI(去掉查询参数) extract_uri() { # 去掉协议和域名,只保留路径部分,再去掉 ? 后的参数 echo "$1" | sed -E 's|https?://[^/]+||' | cut -d'?' -f1 } # 函数:下载文件 download_file() { local url="$1" local uri="$2" local local_path="${LOCAL_ROOT}${uri}" # 如果文件已存在,跳过 if [ -f "$local_path" ]; then return 0 fi # 创建目录 mkdir -p "$(dirname "$local_path")" echo "[$(date)] Downloading: $url" # 使用 curl 下载(-s 静默,-L 跟随重定向,-o 输出文件) if curl -s -L -o "$local_path" "$url"; then echo "[$(date)] Saved to $local_path" else echo "[$(date)] ERROR: Failed to download $url" >> "$ERROR_LOG" fi } # 主循环:tail -F 持续跟踪文件(支持日志轮转) tail -F "$LOG_FILE" | while read line; do # 只处理包含 proxied=1 的行 if echo "$line" | grep -q 'proxied=1'; then url=$(extract_url "$line") if [ -n "$url" ]; then uri=$(extract_uri "$url") download_file "$url" "$uri" fi fi done
nginx.conf:
user root; worker_processes auto; error_log /var/log/nginx/error.log notice; pid /run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; 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 /var/log/nginx/access.log main; sendfile on; #tcp_nopush on; keepalive_timeout 65; #gzip on; include /etc/nginx/conf.d/*.conf; }
start.sh:
#!/bin/bash # 使用 while 循环保证脚本持续运行 while true; do bash /example_cached.sh >> /var/log/nginx/example_cached.log 2>&1 echo "$(date): example_cached.sh exited, restarting in 5 seconds..." >> /var/log/nginx/example_cached.log sleep 5 done & # 前台运行 nginx nginx -g "daemon off;"
compose.yml
services: example-nginx: image: nginx container_name: example-nginx hostname: example-nginx environment: - TZ=Asia/Shanghai privileged: true volumes: - ./example-nginx/conf.d/:/etc/nginx/conf.d/ - ./example-nginx/nginx.conf:/etc/nginx/nginx.conf - ./example-nginx/html/:/html/ - ./example-nginx/start.sh:/start.sh - ./example-nginx/example_cached.sh:/example_cached.sh - ./example-nginx/logs/:/var/log/nginx/ - /etc/uhttpd.crt:/etc/uhttpd.crt - /etc/uhttpd.key:/etc/uhttpd.key ports: - 8820:8820 command: /start.sh restart: always
项目原理:
这个项目的核心原理是 “按需缓存 + 动态代理”,具体流程如下:
Nginx 反向代理
监听 8820 端口(HTTPS),当用户访问 /uploads/ 路径时,默认先尝试从本地 html/www.example.com 目录读取文件。
如果本地不存在(try_files 失败),则通过 @proxy_uploads 将请求代理到上游 https://www.example.com。
自定义日志标记代理行为
在代理逻辑中设置两个自定义变量:$proxied=1 和 $proxy_target(记录实际请求的完整 URL)。
使用专用日志格式 proxy_detailed 将这些信息写入 example.access.log。
后台脚本持续处理日志
example_cached.sh 使用 tail -F 实时跟踪日志文件。
当检测到 proxied=1 的行时,解析出 $proxy_target 中的 URL,提取 URI 路径。
调用 curl 下载该 URL 的内容,保存到本地 html/www.example.com 对应的路径下。
缓存生效
下次同样请求到达时,Nginx 的 try_files 会直接命中本地文件,不再触发代理,实现透明加速。
简而言之:通过日志驱动的方式,将首次代理请求的内容自动持久化到本地,后续请求由静态文件直接响应,降低上游带宽和负载。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。