惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

GbyAI
GbyAI
Vercel News
Vercel News
F
Fortinet All Blogs
Y
Y Combinator Blog
The GitHub Blog
The GitHub Blog
P
Proofpoint News Feed
M
MIT News - Artificial intelligence
Blog — PlanetScale
Blog — PlanetScale
H
Help Net Security
B
Blog RSS Feed
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Engineering at Meta
Engineering at Meta
爱范儿
爱范儿
V
Visual Studio Blog
Stack Overflow Blog
Stack Overflow Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
B
Blog
宝玉的分享
宝玉的分享
云风的 BLOG
云风的 BLOG
U
Unit 42
博客园 - 司徒正美
大猫的无限游戏
大猫的无限游戏
D
DataBreaches.Net
博客园 - 叶小钗
Hugging Face - Blog
Hugging Face - Blog
MongoDB | Blog
MongoDB | Blog
The Cloudflare Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
T
Tailwind CSS Blog
S
Schneier on Security
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Apple Machine Learning Research
Apple Machine Learning Research
Attack and Defense Labs
Attack and Defense Labs
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
A
About on SuperTechFans
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
L
LangChain Blog
博客园 - 【当耐特】
Recorded Future
Recorded Future
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
aimingoo的专栏
aimingoo的专栏
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Hacker News: Ask HN
Hacker News: Ask HN
Jina AI
Jina AI
Schneier on Security
Schneier on Security
W
WeLiveSecurity
Microsoft Security Blog
Microsoft Security Blog
博客园_首页
博客园 - 聂微东

博客园 - Nihaorz

告别闪烁,拥抱流畅:在 Windows Terminal 中完美配置 Cygwin 环境 创建 docker ipvlan,让 docke 容器获取独立ip 电犀牛 R68s iStoreOS 2.5G 网口速率优化 解决 openwrt ssh 命令行终端 home、end 键不可用问题 一键添加视频封面脚本 ffmpeg 转码参数 docker save 远程 ssh 主机直接 load,不产生本地文件 AutoHotKey 脚本 - win10 自动连接无线显示器 SSH 登录/退出实时监控脚本 OpenClaw 安装部署,配置 deepseek curl 断点续传下载 debian iso 镜像下载地址 linux 安装 zerotier,加入网络 基于 Fail2ban 的 SSH 入侵自动反制方案 ssh 配置密钥登录,关闭密码登录 memc - 基于 shell 的交互式清理内存脚本 基于 Fail2ban 的 OpenWRT SSH 入侵自动反制方案 Linux Screen 命令速查 使用 ofelia 在 docker 容器中执行计划任务 linux 磁盘挂载示例
Nginx 透明代理 + 自动回源存储:访问即缓存,落盘即文件
Nihaorz · 2026-04-03 · via 博客园 - Nihaorz

项目结构:

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

项目原理:

这个项目的核心原理是 “按需缓存 + 动态代理”,具体流程如下:

  1. Nginx 反向代理

    • 监听 8820 端口(HTTPS),当用户访问 /uploads/ 路径时,默认先尝试从本地 html/www.example.com 目录读取文件。

    • 如果本地不存在(try_files 失败),则通过 @proxy_uploads 将请求代理到上游 https://www.example.com

  2. 自定义日志标记代理行为

    • 在代理逻辑中设置两个自定义变量:$proxied=1 和 $proxy_target(记录实际请求的完整 URL)。

    • 使用专用日志格式 proxy_detailed 将这些信息写入 example.access.log

  3. 后台脚本持续处理日志

    • example_cached.sh 使用 tail -F 实时跟踪日志文件。

    • 当检测到 proxied=1 的行时,解析出 $proxy_target 中的 URL,提取 URI 路径。

    • 调用 curl 下载该 URL 的内容,保存到本地 html/www.example.com 对应的路径下。

  4. 缓存生效

    • 下次同样请求到达时,Nginx 的 try_files 会直接命中本地文件,不再触发代理,实现透明加速。

简而言之:通过日志驱动的方式,将首次代理请求的内容自动持久化到本地,后续请求由静态文件直接响应,降低上游带宽和负载。