在 Alpine Linux 下,如果只是临时分享目录,python3 -m http.server 很方便;但如果要长期对外提供文件浏览和下载,Nginx 更适合。它可以处理 HTTPS、目录浏览、Basic Auth、访问日志、限流、安全响应头和强制下载,资源占用也很低。
本文以 fetch.jgaga.tk 为示例域名,Web 根目录为 /var/www/fetch.jgaga.tk。实际使用时,把域名和目录替换成自己的即可。
准备证书
正式对外提供 HTTPS 之前,需要先给域名签发证书。可以使用你现有的一键脚本,也可以使用 acme.sh、lego 或 certbot。
本文后续示例假设证书和私钥已经放在下面的位置:
/etc/ssl/fetch.jgaga.tk/fetch.jgaga.tk.crt
/etc/ssl/fetch.jgaga.tk/fetch.jgaga.tk.key如果你的一键脚本生成的是其他路径,只需要修改 Nginx 配置里的 ssl_certificate 和 ssl_certificate_key。
安装并启用 Nginx
Alpine 使用 apk 安装软件,服务管理使用 OpenRC:
apk update
apk add nginx openssl logrotate
rc-update add nginx default创建 Web 根目录:
mkdir -p /var/www/fetch.jgaga.tk
chown -R root:nginx /var/www/fetch.jgaga.tk这里推荐让目录归属为 root:nginx。Nginx worker 以 nginx 用户运行,只需要读取文件即可;后面如果要让普通用户上传文件,再单独调整目录权限。
创建认证密码文件
如果目录要对外开放,建议先加 Basic Auth。下面以用户名 admin 为例:
printf "admin:%s\n" "$(openssl passwd -apr1)" > /etc/nginx/.htpasswd
chown root:nginx /etc/nginx/.htpasswd
chmod 640 /etc/nginx/.htpasswd执行第一条命令时,系统会提示输入并确认密码,输入过程不会显示字符,这是正常的。
如果要换用户名,修改冒号前面的 admin 后重新生成即可:
printf "your_username:%s\n" "$(openssl passwd -apr1)" > /etc/nginx/.htpasswd
chown root:nginx /etc/nginx/.htpasswd
chmod 640 /etc/nginx/.htpasswd创建日志目录
为了让日志路径和后面的 logrotate 规则一致,先显式创建 Nginx 日志目录:
mkdir -p /var/log/nginx
chown root:nginx /var/log/nginx
chmod 755 /var/log/nginxNginx 完整配置
编辑 /etc/nginx/nginx.conf,可以按下面这份配置整理。它包含:
nvim /etc/nginx/nginx.conf- HTTP 自动跳转 HTTPS
- Let’s Encrypt ACME 验证目录放行
- TLS 1.2 / TLS 1.3
- HTTP/2
- 目录浏览
- Basic Auth
- 敏感文件拦截
- Gzip
- 单 IP 并发、请求频率和下载速度限制
- 指定格式强制下载
user nginx;
worker_processes auto;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
sendfile on;
keepalive_timeout 65;
gzip on;
gzip_vary on;
gzip_comp_level 5;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/x-javascript application/xml application/xml+rss application/x-sh application/x-yaml;
# 限速与请求频率的共享内存区域定义
limit_conn_zone $binary_remote_addr zone=addr_conn:10m;
limit_req_zone $binary_remote_addr zone=addr_req:10m rate=5r/s;
# ========================================================
# 1. 80 端口:负责 Let's Encrypt 证书验证及全站强制跳转 HTTPS
# ========================================================
server {
listen 80;
listen [::]:80;
server_name fetch.jgaga.tk;
location /.well-known/acme-challenge/ {
root /var/www/fetch.jgaga.tk;
}
location / {
return 301 https://$host$request_uri;
}
}
# ========================================================
# 2. 10521 端口:全站安全、密码认证、完全去重后的限速下载服务
# ========================================================
server {
listen 10521 ssl;
listen [::]:10521 ssl;
http2 on;
server_name fetch.jgaga.tk;
# SSL 证书配置
ssl_certificate /etc/ssl/fetch.jgaga.tk/fetch.jgaga.tk.crt;
ssl_certificate_key /etc/ssl/fetch.jgaga.tk/fetch.jgaga.tk.key;
# SSL 安全参数
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m;
ssl_session_tickets off;
# 【优化】提取至 server 全局:全站通用的安全响应头
add_header Strict-Transport-Security "max-age=63072000" always;
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options SAMEORIGIN;
add_header X-XSS-Protection "1; mode=block";
# 【优化】提取至 server 全局:网页与文件下载统一要求密码认证
auth_basic "Restricted Access";
auth_basic_user_file /etc/nginx/.htpasswd;
# 【优化】提取至 server 全局:统一限速规则
# 默认方案:前 500MB 全速,后续 500KB/s,适合公开下载或带宽有限的 VPS
limit_conn addr_conn 3;
limit_req zone=addr_req burst=10 nodelay;
limit_rate_after 500m;
limit_rate 500k;
# 不限速方案:如果只想限制连接数/请求频率,不限制下载速度,
# 可以保留上面的 limit_conn / limit_req,并注释或删除上面两行限速指令:
# limit_rate_after 500m;
# limit_rate 500k;
# 网站根目录
root /var/www/fetch.jgaga.tk;
index index.html index.htm;
# 拒绝访问以 . 开头的隐藏文件(如 .htaccess, .git 等)
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
# 拒绝访问敏感后缀文件
location ~ \.(key|pem|conf|sh|sql)$ {
deny all;
access_log off;
log_not_found off;
}
# 核心处理块:包含全站文件浏览列表
location / {
autoindex on;
autoindex_exact_size off;
autoindex_localtime on;
charset utf-8;
# 【无重绝招】如果是特定的媒体或文档文件,直接在此强制触发下载。
# 这样既实现了图片/视频强制下载,又完美继承了上面所有的安全头、认证和限速,绝无重复代码。
if ($request_filename ~* \.(jpg|jpeg|png|gif|mp4|mkv|avi|mp3|pdf|txt)$) {
add_header Content-Disposition "attachment";
}
}
}
}注意:使用正则 location 匹配强制下载文件时,请把认证和限流也写进去。否则直接访问这些文件 URL 时,可能绕过 location / 中的控制规则。
测试并启动服务
nginx -t
rc-service nginx start后续修改配置后,使用 reload 即可:
nginx -t
rc-service nginx reload打开 https://fetch.jgaga.tk:10521 后,应该会先弹出 Basic Auth 登录框。认证通过后,可以看到 Nginx 的目录列表。
修复点击文件 403 的权限问题
如果目录列表能显示文件名,但点击 .jpg 等文件时返回 403 Forbidden,通常是文件本身权限不足。autoindex 能列目录,不代表 Nginx 一定能读取文件内容。
推荐先把目录所有权整理为 root:nginx:
chown -R root:nginx /var/www/fetch.jgaga.tk如果仍然有权限问题,再统一修正目录和文件权限:
find /var/www/fetch.jgaga.tk -type d -exec chmod 755 {} \;
find /var/www/fetch.jgaga.tk -type f -exec chmod 644 {} \;执行完不需要重启 Nginx,刷新浏览器即可。
让普通用户管理文件目录
普通用户不能直接运行当前这套 Nginx 服务,因为 80 和 443 属于特权端口,证书私钥和系统日志也需要更高权限。正确做法是:Nginx 仍由 root 启动,worker 自动降权为 nginx;普通用户只负责上传和管理 Web 根目录里的文件。
假设普通用户是 felix,先把它加入 nginx 组:
addgroup felix nginx再允许同组用户写入目录:
chmod -R 775 /var/www/fetch.jgaga.tk
chmod g+s /var/www/fetch.jgaga.tkchmod g+s 会让目录中新建的文件继承 nginx 组,避免后续文件归属变得混乱。
修改用户组后,旧 SSH 会话不会自动刷新身份。重新登录,或在当前终端执行:
su - felix然后用 id 确认输出里包含 nginx 组。
配置日志按天轮转
Alpine 没有 FreeBSD 的 newsyslog,这里使用 logrotate。前面已经通过 apk add logrotate 安装。
创建 /etc/logrotate.d/nginx:
/var/log/nginx/*.log {
daily
rotate 30
missingok
notifempty
compress
delaycompress
create 0644 root nginx
sharedscripts
postrotate
[ -s /run/nginx/nginx.pid ] && kill -USR1 "$(cat /run/nginx/nginx.pid)"
endscript
}参数说明:
daily:每天轮转。rotate 30:保留最近 30 天日志。compress和delaycompress:压缩旧日志,并延后一轮压缩当前刚切走的文件。create 0644 root nginx:轮转后创建新的日志文件。kill -USR1:通知 Nginx master 重新打开日志文件,无需重启服务。
测试配置:
logrotate -d /etc/logrotate.d/nginx如果没有语法错误,即可交给系统定时任务自动执行。Alpine 通常通过 /etc/periodic/daily/logrotate 调用 logrotate;如果你的系统没有这个文件,可以手动确认 crond 是否启用:
rc-update add crond default
rc-service crond start强制下载指定文件类型
如果希望点击图片、视频、PDF、文本文件时不要在浏览器中预览,而是直接弹出下载,可以使用:
location ~* \.(jpg|jpeg|png|gif|mp4|mkv|avi|mp3|pdf|txt)$ {
add_header Content-Disposition "attachment";
auth_basic "Restricted Access";
auth_basic_user_file /etc/nginx/.htpasswd;
limit_conn addr_conn 3;
limit_req zone=addr_req burst=10 nodelay;
# 默认限速:前 500MB 全速,后续 500KB/s。
# 如果要不限速,注释或删除下面两行即可。
limit_rate_after 500m;
limit_rate 500k;
}如果你的 HTTPS server 里已经有全局安全响应头,建议也在这个正则 location 中补齐一份 add_header。Nginx 的 add_header 在不同层级混用时容易出现继承行为不符合直觉的情况,直接写全更稳。
可选:内网免密、外网认证
如果以后想让内网直接访问,外网才需要密码,可以在 location / 中使用 satisfy any:
location / {
autoindex on;
autoindex_exact_size off;
autoindex_localtime on;
charset utf-8;
satisfy any;
allow 192.168.100.0/24;
allow 10.20.20.0/24;
allow fd88::/64;
allow 127.0.0.1;
deny all;
auth_basic "Restricted Access";
auth_basic_user_file /etc/nginx/.htpasswd;
}本文完整配置没有启用这段规则。如果你没有固定内网网段需求,保持 Basic Auth 全局生效即可。
可选:配合 fail2ban 封禁限流 IP
Nginx 触发 limit_req 或 limit_conn 后,会在错误日志里写入 limiting requests 或 limiting connections。可以用 fail2ban 继续联动封禁。
安装:
apk add fail2ban
rc-update add fail2ban default创建 /etc/fail2ban/filter.d/nginx-limit.conf:
[Definition]
failregex = ^\s*\[error\] \d+#\d+: \*\d+ limiting (requests|connections).*? client: <HOST>,
ignoreregex =在 /etc/fail2ban/jail.local 中加入:
[nginx-limit]
enabled = true
filter = nginx-limit
port = http,https
logpath = /var/log/nginx/error.log
findtime = 60
maxretry = 5
bantime = 3600测试正则并重载:
fail2ban-regex /var/log/nginx/error.log /etc/fail2ban/filter.d/nginx-limit.conf
rc-service fail2ban restartAlpine 常见防火墙可能是 nftables、iptables 或 awall,封禁动作要按你实际使用的防火墙调整。
可选:开启 Linux BBR
Alpine 是 Linux 系统,BBR 的开启方式和 FreeBSD 不同。先确认内核支持:
sysctl net.ipv4.tcp_available_congestion_control如果输出里包含 bbr,可以临时开启:
modprobe tcp_bbr 2>/dev/null || true
sysctl -w net.core.default_qdisc=fq
sysctl -w net.ipv4.tcp_congestion_control=bbr验证:
sysctl net.ipv4.tcp_congestion_control如果输出为下面这样,就说明已经生效:
net.ipv4.tcp_congestion_control = bbr永久生效:
cat > /etc/sysctl.d/99-bbr.conf <<'EOF'
net.core.default_qdisc=fq
net.ipv4.tcp_congestion_control=bbr
EOF
sysctl --system如果你的 Alpine 内核没有内置或加载 BBR,sysctl 会报 No such file or directory 或可用算法中没有 bbr。这种情况需要更换支持 BBR 的内核,不能只靠 Nginx 配置解决。
总结
这套配置可以把 Alpine 上的一个普通目录整理成可长期使用的 HTTPS 文件浏览服务:
- Nginx 负责目录浏览和静态文件传输。
- TLS 与安全响应头保证基础 HTTPS 安全性。
- Basic Auth 防止目录裸奔。
- 敏感文件拦截避免
.htpasswd、私钥、配置文件等被访问。 logrotate负责日志轮转。- Gzip 和限流分别处理传输效率与带宽保护。
- 强制下载规则让图片、视频、PDF 等文件点击后直接下载。
完成配置后,记得每次修改都先执行:
nginx -t确认语法无误后再 reload。