返回

Nginx下Laravel子目录部署:告别Access Denied!

php

Nginx 下 Laravel 子目录部署,告别 Access Denied!

老项目要加新功能了?打算用 Laravel 搞定,本地 XAMPP 配 Apache 跑得飞起,一到线上 Nginx 环境就 Access Denied?别慌,这篇博客帮你捋清楚 Nginx 子目录部署 Laravel 的那些坑。

问题出在哪?

你遇到的问题,简单来说,就是 Nginx 没能正确地将请求转发给 Laravel 的入口文件 index.php。 默认情况下,Nginx 会尝试查找与 URL 匹配的物理文件或目录。当 Laravel 部署在子目录下时,直接访问 mysite.com/2015,Nginx 找不到对应的文件或目录,就会拒绝访问。或者,即使找到了 /2015这个物理目录,你配置的方式,会导致无限循环重定向。

解决方案,安排!

下面给出几种解决思路,总有一款适合你。

方案一:alias + try_files + 重写规则 (推荐)

这是最常用的方式,也是我最推荐的方式。它的核心思想是:

  1. alias 指定目录:alias 指令告诉 Nginx,/2015 这个路径实际上对应服务器上的哪个目录。
  2. try_files 尝试匹配: 优先尝试直接访问请求的文件或目录。
  3. 重写规则兜底: 如果找不到文件,就把请求重写到 Laravel 的入口文件 index.php,并把原始请求路径作为参数传递过去。

配置示例:

server {
    listen 80;
    server_name am2.aminversiones.com;
    root /home/forge/am2.aminversiones.com;

    index index.html index.htm index.php;

    charset utf-8;

    client_max_body_size 300M;

    location / {
        try_files $uri $uri/ /index.php?q=$uri&$args;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    access_log off;
    error_log  /var/log/nginx/am2.aminversiones.com-error.log error;

    error_page 404 /index.php;

  # PHP 解释器配置, 请按照你的环境调整.
    location ~ \.php$ {
       fastcgi_split_path_info ^(.+\.php)(/.+)$;
       fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock; # <-- 这里按需修改
       fastcgi_index index.php;
       fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
       include fastcgi_params;
    }

    # Remove trailing slash to please routing system.
    if (!-d $request_filename) {
        rewrite     ^/(.+)/$ /$1 permanent;
    }
    # Laravel 子目录配置
    location ^~ /2015 {
        alias /home/forge/am2.aminversiones.com/2015/public;
        try_files $uri $uri/ @laravel;

        location ~* \.php$ {
            fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock; # <-- 这里按需修改
            fastcgi_split_path_info ^(.+\.php)(.*)$;
            include fastcgi_params;
             fastcgi_param SCRIPT_FILENAME $request_filename;
        }
    }

    location @laravel {
        rewrite ^/2015/(.*)$ /2015/index.php?/$1 last;
    }
     location ~ /\.ht {
        deny all;
    }

}

代码解读:

  • location ^~ /2015 匹配以 /2015 开头的请求。 ^~ 表示如果匹配到这个 location,就不再检查其他正则表达式 location。
  • alias /home/forge/am2.aminversiones.com/2015/public;/2015 开头的请求映射到服务器的 /home/forge/am2.aminversiones.com/2015/public 目录。 重要提示: 请确保这个路径正确指向你的 Laravel 项目的 public 目录!
  • try_files $uri $uri/ @laravel;
    • $uri:表示当前请求的 URI(不带参数)。
    • $uri/:尝试在 $uri 后面加上 /,看看是不是一个目录。
    • @laravel:如果前面两个都没找到,就跳转到名为 @laravel 的 location。
  • location @laravel 定义一个名为 @laravel 的 location。
  • rewrite ^/2015/(.*)$ /2015/index.php?/$1 last; 这行是关键!它将 /2015/xxx 这样的请求重写为 /2015/index.php?/xxx
    • ^/2015/(.*)$: 匹配/2015/后所有字符,并将匹配内容存入$1.
    • /2015/index.php?/$1: 重定向的目标地址, 注意 ? 后面有个 /.
    • last:停止处理后续 rewrite 指令,并用新的 URI 重新搜索 location。
  • fastcgi_param SCRIPT_FILENAME $request_filename;:location ~* \.php$ 中设置 SCRIPT_FILENAME 使用 $request_filename. 这是因为使用 alias 以后, $document_root 变量不会按照预想的方式改变, 因此无法正确传递给 php 解释器文件地址。
  • fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;: 在你的环境,这个路径很可能需要改变。 找到你正确的php-fpm.sock 文件的地址.

修改配置后,别忘了重载 Nginx:

sudo nginx -t  # 检查配置文件语法
sudo systemctl reload nginx # 重载配置

方案二:使用 try_files 直接判断

这种方法相对简单,但是需要对try_files有较深入的理解。

server {
    # ... 其他配置 ...

     location ~ \.php$ {
       fastcgi_split_path_info ^(.+\.php)(/.+)$;
       fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock; # <-- 这里按需修改
       fastcgi_index index.php;
       fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
       include fastcgi_params;
    }

    # ... 其他配置 ...

    location /2015 {
       try_files $uri $uri/ /2015/index.php?$query_string;
    }

        location ~ /\.ht {
        deny all;
    }

}

原理:

  • /2015 : 表示匹配所有以/2015开头的路径。
  • try_files $uri $uri/ /2015/index.php?$query_string : 如果请求的文件找不到, 则尝试访问 /2015/index.php并将参数传入.

注意: 这种配置相对精简, 但是必须保证/2015 是实际存在目录, Nginx才可以找到/2015/index.php

修改配置后,别忘了重载 Nginx:

sudo nginx -t
sudo systemctl reload nginx

方案三:修改 Laravel 的 index.php(不推荐)

这种方法不推荐 ,因为它需要修改 Laravel 核心文件,可能导致升级问题。但作为一种思路,也介绍一下。

你可以修改 Laravel 项目 public/index.php 文件,在其中添加代码来处理子目录的路径问题。大致思路是:

  1. 获取当前请求的 URI。
  2. 判断 URI 是否以 /2015 开头。
  3. 如果是,则修改 $request 对象的路径信息,去掉 /2015 前缀。

具体代码我就不写了,因为真的不推荐 这种方式。

安全建议

  • 隐藏入口文件: 将除了 public 目录之外的所有 Laravel 文件移到 Web 根目录之外,可以提高安全性。 也就是调整服务器设置,修改 root 到public目录.
  • 限制文件访问: 确保 Nginx 配置中限制了对敏感文件(如 .env.git)的访问。一般通过设置 location ~ /\. { deny all; } 来拒绝访问所有 . 开头的文件
  • 最小权限原则 确保运行PHP的账户只拥有运行需要的最低权限.

进阶技巧

多项目共存

如果你有多个 Laravel 项目,需要分别部署在不同的子目录下,可以用类似的方法。只需要为每个项目配置一个独立的 location 块,并分别设置 alias 和重写规则即可。 只需要在server 块里增加:

    location ^~ /2016 {
        alias /home/forge/am2.aminversiones.com/2016/public;
        try_files $uri $uri/ @laravel2016;

        location ~* \.php$ {
            fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock; # <-- 这里按需修改
            fastcgi_split_path_info ^(.+\.php)(.*)$;
            include fastcgi_params;
            fastcgi_param SCRIPT_FILENAME $request_filename;
        }
    }

    location @laravel2016 {
        rewrite ^/2016/(.*)$ /2016/index.php?/$1 last;
    }

当然,/2016需要是实际存在并且放置了Laravel代码的文件夹。

这篇博客从问题分析到解决方案,再到安全建议和进阶技巧,希望能帮到你。 祝你部署顺利!