You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何正确在子目录部署Laravel应用?

问题描述

我要把Laravel应用部署在https://my-domain.com/demo/,当前已有Nginx运行在https://my-domain.com/,所以在Nginx配置里加了以下内容,把/demo的流量代理到应用服务器:

upstream demo_app {
    server 10.x.x.x:8080;
    keepalive 16;
}

server {
    location = /demo {
        return 301 /demo/;
    }
    location ^~ /demo/ {
        proxy_pass http://demo_app/;
        # Pass original host + protocol so Laravel can build correct URLs
        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host  $host;
        proxy_set_header X-Forwarded-Port  $server_port;

        # Optional but useful: tell the app it's mounted under a prefix
        proxy_set_header X-Forwarded-Prefix /demo;

        proxy_http_version 1.1;
        proxy_set_header Connection "";
    }
    # Rest of the server config
}

我原本以为只要把my-domain.com服务器添加到TrustProxies.php,再把APP_URL和VITE_BASE_PATH设成包含子目录就能正常运行,但发现必须在中间件里写urlResolver手动生成正确URL,才能保证URL生成和跳转正常:

class HandleInertiaRequests extends Middleware
{
    /**
     * The root template that is loaded on the first page visit.
     *
     * @var string
     */
    protected $rootView = 'app';

    /**
     * Determine the current asset version.
     */
    public function version(Request $request): ?string
    {
        return parent::version($request);
    }

    /**
     * Define the props that are shared by default.
     *
     * @return array<string, mixed>
     */
    public function share(Request $request): array
    {
        return [
            ...parent::share($request),
            'auth' => [
                'user' => $request->user(),
            ],
            'ziggy' => fn() => [
                ...(new Ziggy)->toArray(),
                'url' => rtrim(config('app.url'), '/'),
                'location' => $request->url(),
            ],
            'flash' => [
                'error_message' => fn() => $request->session()->get('error_message'),
            ],
            'active_route' => [
                'current' => Route::currentRouteName(),
            ],
        ];
    }

    public function urlResolver()
    {
        return function (Request $request) {
            $prefix = rtrim($request->headers->get('X-Forwarded-Prefix', ''), '/');

            // Path and query as Laravel sees them (after proxy_pass)
            $path  = $request->getPathInfo();
            $query = $request->getQueryString();

            // Build the URL Inertia should think we're at
            $url = ($prefix ? $prefix : '') . $path;
            if ($query) {
                $url .= '?' . $query;
            }

            return $url;
        };
    }
}

我也已经把前端代理和头部信息加到TrustProxies.php里了:

<?php

namespace App\Http\Middleware;

use Illuminate\Http\Middleware\TrustProxies as Middleware;
use Illuminate\Http\Request;

class TrustProxies extends Middleware
{
    /**
     * The trusted proxies for this application.
     *
     * @var array<int, string>|string|null
     */
    protected $proxies = [
        '10.0.0.0/8',
    ];

    /**
     * The headers that should be used to detect proxies.
     *
     * @var int
     */
    protected $headers = Request::HEADER_X_FORWARDED_FOR |
        Request::HEADER_X_FORWARDED_HOST |
        Request::HEADER_X_FORWARDED_PORT |
        Request::HEADER_X_FORWARDED_PROTO |
        Request::HEADER_X_FORWARDED_PREFIX |
        Request::HEADER_X_FORWARDED_AWS_ELB;
}

我是Laravel新手,当前方案能运行但不够优雅,想知道有没有更优方案,能让Ziggy、Inertia、Vite及其他工具链自动识别应用运行在子目录?

优化方案

不需要手动编写urlResolver,通过以下配置可以让整个工具链自动识别子目录:

1. Laravel 核心配置

确保APP_URL正确设置为子目录地址:

APP_URL=https://my-domain.com/demo

你的TrustProxies.php配置已经正确包含了X-Forwarded-Prefix,Laravel会自动读取这个头部来修正请求的URL信息,无需额外处理。

2. Ziggy 配置

Ziggy可以直接读取APP_URL和请求的前缀信息,不需要在share方法里手动覆盖url和location。只需确保在前端正确引入Ziggy的配置,或者在后端共享时直接传递默认的Ziggy数组:

'ziggy' => fn() => (new Ziggy)->toArray(),

如果需要更灵活的配置,可以在config/ziggy.php里设置prefix:

return [
    'prefix' => env('APP_URL') !== 'http://localhost' ? '/demo' : '',
    // 其他配置...
];

3. Inertia 配置

Inertia会自动使用Laravel生成的正确URL,只要Laravel本身能识别子目录(通过X-Forwarded-Prefix和APP_URL),就不需要自定义urlResolver。移除你添加的urlResolver方法即可,Inertia会自动处理URL生成。

4. Vite 配置

确保VITE_BASE_PATH设置正确:

VITE_BASE_PATH="/demo"

然后在vite.config.js里配置base选项:

import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';

export default defineConfig({
    plugins: [
        laravel({
            input: ['resources/css/app.css', 'resources/js/app.js'],
            refresh: true,
        }),
    ],
    base: process.env.VITE_BASE_PATH,
});

在Blade模板里引入Vite资源时,使用Laravel的@vite指令即可,它会自动加上前缀:

@vite(['resources/css/app.css', 'resources/js/app.js'])

5. 路由配置

不需要修改路由文件,Laravel会自动处理子目录的路由解析,因为Nginx已经把/demo/下的请求代理到应用根目录,应用里的路由可以保持原本的写法(比如Route::get('/', ...)对应https://my-domain.com/demo/)。

内容的提问来源于stack exchange,提问作者MorayM

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.01 22:08:10