墨林 - Molinink · 作者 Kira · https://www.molinink.com

============================================================
服务器配置:两条必加 + 两条可选 + 一条看插件
============================================================

程序是单入口的:除了真实存在的文件,所有地址都要交给站点根目录的 index.php 处理。
这一步 Web 服务器不做,程序自己办不到,所以必须由你在面板里粘一次:

· 第一条(伪静态):让 /admin、/blog、文章页这些"没有真实文件"的地址能进到 index.php。
  **不粘这一条,装完能进前台首页,但后台 /admin 和所有文章页都是 404。**
· 第二条(/storage/):安全项。storage/ 就在站点根目录里,不拦的话,备份包、日志
  可能被人顺着地址下载走。
· 第三、四条是性能项,可选:让 nginx 直接发图片与主题静态文件,省一次 PHP。
· 第八条只在**装了「下载中心」插件**时才要看:安装包要么交给 nginx 内部跳转发,
  要么在后台把「经由 Nginx 内部跳转」关掉,走程序自己发。两种都行,区别见第八节。

写在哪里(宝塔面板)
--------
· 第一条:网站 → 设置 → 伪静态,把文中那段粘进去,保存(不用改配置文件,面板会接管)。
· 第二条:网站 → 设置 → 配置文件,粘到 server { ... } 里面,保存。
· 自己维护 Nginx:两条都粘到对应的 server { } 块里。
· 改完记得:重载 / 重启 Web 服务(Nginx: reload 即可)。

------------------------------------------------------------
一、伪静态 ——(必加)让后台与文章页能打开
------------------------------------------------------------
程序只有一个入口 index.php,地址形如 /admin、/blog、/2026/10/hello 在磁盘上并没有对应文件。
nginx 找不到文件就回 404,于是必须让它"找不到就交给 index.php"。

宝塔:网站 → 设置 → 伪静态,粘贴下面这一段,保存:

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    # 后台走程序路由。站点根下真有一个 admin/ 目录(css/js/模板,没有 index 文件),
    # 只给 location / 的话 nginx 会按目录索引处理 —— 点「进入后台」直接 403 Forbidden。
    location = /admin {
        try_files $uri /index.php$is_args$args;
    }
    location = /admin/ {
        return 301 /admin;
    }

自己维护 nginx 的话,放进 server { } 里即可(与原有的 location ~ \.php$ 并存,不冲突)。

------------------------------------------------------------
二、/storage/ ——(必加)别让人把你的备份包和日志下载走
------------------------------------------------------------
storage/ 里是:全站备份包(整库 + 全部文件)、运行日志、缓存、以及你填过的远端备份凭据。
它在站点根目录内,不拦的话,知道地址的人就能直接把备份拖走。

宝塔:网站 → 设置 → 配置文件,粘进 server { } 里:

    location ^~ /storage/ {
        return 404;
    }

------------------------------------------------------------
三、/uploads/ ——(可选)让图片走 nginx,省一次 PHP
------------------------------------------------------------
程序把上传的图片存在 storage/uploads/ 下(这个目录本身不对外),对外给一个 /uploads/ 前缀。
**不加这一段也能出图** —— 程序里有一条兜底路由负责发这些文件;加了只是更快(nginx 直接发,
不必进 PHP),并带上 30 天的长缓存头。

Nginx(宝塔):

    location ^~ /uploads/ {
        # 把下面这行换成你站点的真实路径
        alias /www/wwwroot/你的站点目录/storage/uploads/;
        try_files $uri =404;
        expires 30d;
        add_header Cache-Control "public, immutable";
        # 上传目录里只该有图片等静态文件,任何脚本一律不给执行
        location ~* \.(php|phtml|phar|php\d)$ { deny all; }
    }

------------------------------------------------------------
四、/themes/ ——(可选)主题静态资源长缓存
------------------------------------------------------------
主题的 CSS / JS / 字体地址都带 ?v=(内核按文件修改时间自动生成),文件一改地址就变。
所以这里可以放心让浏览器缓存一年,省掉重复下载和 304 往返。

Nginx(宝塔):

    location ^~ /themes/ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }

------------------------------------------------------------
装完自己核一遍(在浏览器地址栏敲)
------------------------------------------------------------
· 你的站点/                     → 200,能看到站点首页
· 你的站点/admin                → 200,能看到后台登录页(404 = 第一条没生效)
· 你的站点/storage/backups/     → 应该是 404
· 你的站点/storage/logs/        → 应该是 404
· 你的站点/uploads/某个图片      → 200,能看图(不加第三条也应该能看)
· 你的站点/__market/随便一个文件名 → 应该是 404(装了下载中心插件才需要核,见第八节)

------------------------------------------------------------
五、Apache(仅作参考)
------------------------------------------------------------
站点根目录放一个 .htaccess(要求 AllowOverride All):

    <IfModule mod_rewrite.c>
        RewriteEngine On
        RewriteCond %{REQUEST_FILENAME} !-f
        RewriteCond %{REQUEST_FILENAME} !-d
        RewriteRule ^ index.php [L]
    </IfModule>

    RedirectMatch 404 ^/storage/

/uploads/ 想走 Apache 直发,需要一条别名(Alias 只能写在虚拟主机或主配置里):

    Alias /uploads/ /你的站点真实路径/storage/uploads/
    <Directory "/你的站点真实路径/storage/uploads/">
        Require all granted
        <FilesMatch "\.(php|phtml|phar)$">
            Require all denied
        </FilesMatch>
    </Directory>

------------------------------------------------------------
六、备份文件名已经改成不可猜
------------------------------------------------------------
备份包的名字形如 backup-molin-20261017-153012-9f3c1a7e-full.tar.gz:
中间那段 8 位随机串让"顺着时间猜文件名"失效(旧备份不受影响,仍会列出来)。
但这只是第二道保险,**第二条规则该配还是要配**。

------------------------------------------------------------
七、还建议顺手关掉的两件
------------------------------------------------------------
· 目录列表(autoindex):关掉,免得有人翻到 uploads/ 里的文件清单。
· 错误显示:生产环境把 display_errors 关掉(程序会写日志,不会少东西)。

------------------------------------------------------------
八、下载中心(装了下载中心插件才需要)
------------------------------------------------------------
下载中心的安装包放在 storage/market/packages/ 下。storage/ 整段已经被第二条拦成 404,
这些包对外没有地址 —— 程序把文件交出去靠的是一条"只准程序内部跳转"的通道。

后台「下载中心 → 设置」里那一项「经由 Nginx 内部跳转」(就是"让 nginx 发文件"那一项)
默认是**关**的,两种走法自己挑:

· 关着(默认,省事):程序自己分块读出文件再发,不用碰 Nginx,装完就能下。
  代价是大包(几十上百 MB)要占着 PHP 进程慢慢吐,慢一些、并发多了更吃内存。
· 开着(快):程序只回一句"去这个内部地址取",文件由 nginx 直接发,快、稳、不占 PHP。
  **但必须加下面这段,不加就是下载 404。**

想快就加这一段,Nginx(宝塔:网站 → 设置 → 配置文件,粘进 server { } 里):

    location ^~ /__market/ {
        # 只给程序内部跳转,外面直接访问一律 404
        internal;
        # 把下面这行换成你站点的真实路径
        alias /www/wwwroot/你的站点目录/storage/market/packages/;
        add_header Cache-Control "no-store" always;
        add_header X-Content-Type-Options "nosniff" always;
        limit_rate 8m;          # 单连接限速,防带宽打满
    }

顺带(可选,不加也能用)给下载入口加一层兜底限速。先在 nginx.conf 的 http { } 里声明:

    limit_req_zone $binary_remote_addr zone=molin_dl:10m rate=30r/m;

再在 server { } 里加(这两段要一起加,只加后面那段 nginx 会因为认不得 molin_dl 直接起不来):

    location ~ ^/dl/ {
        limit_req zone=molin_dl burst=20 nodelay;
        try_files $uri /index.php$is_args$args;
    }

一句话:**不想改 Nginx,就去后台把「经由 Nginx 内部跳转」关掉(它默认就是关的)**,
功能一样下载得到,只是大包慢一点;要性能就加这段。
