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

Laravel 5 SaaS单应用多数据库场景下的软链接配置求助

我来帮你解决这个Laravel SaaS的子域名与.env隔离问题,你之前的核心问题在于直接把整个主应用软链到客户目录,导致.env变成了共享的软链,自然修改会同步到主应用。下面是具体的解决方案:

核心思路

让每个客户目录只保留独立的.env文件,其余所有应用文件都通过软链指向主应用,但绝对不能将主应用的.env也软链过来,确保每个客户拥有专属的环境配置文件。

具体步骤

1. 创建客户目录与独立.env文件

首先为客户A创建专属目录,然后基于主应用的模板生成独立的.env文件:

# 创建客户目录
mkdir -p /var/www/clients/clientA

# 从主应用复制.env模板并初始化客户配置
cp /var/www/main/.env.example /var/www/clients/clientA/.env

# 编辑客户A的.env,配置专属数据库、APP_URL等信息
nano /var/www/clients/clientA/.env

2. 软链主应用文件(排除.env)

进入客户目录,将主应用的所有文件/目录逐个软链过来,但跳过主应用的.env:

cd /var/www/clients/clientA

# 遍历主应用的所有文件/目录,软链到客户目录
for item in /var/www/main/*; do
    # 获取文件/目录名称
    item_name=$(basename "$item")
    # 跳过主应用的.env文件
    if [ "$item_name" != ".env" ]; then
        ln -s "$item" "./$item_name"
    fi
done

这样客户目录下的.env是完全独立的,其他文件(比如app/、public/、artisan等)都是指向主应用的软链,修改客户的.env不会对主应用造成任何影响。

3. 自动化批量创建客户(可选)

如果需要创建大量客户,手动操作太繁琐,可以写一个shell脚本create_client.sh来一键生成:

#!/bin/bash

# 检查是否传入客户名称参数
if [ -z "$1" ]; then
    echo "请传入客户名称,例如:./create_client.sh clientB"
    exit 1
fi

CLIENT_NAME=$1
MAIN_APP="/var/www/main"
CLIENT_DIR="/var/www/clients/$CLIENT_NAME"

# 创建客户目录
mkdir -p "$CLIENT_DIR"

# 复制.env模板并自动替换关键配置
cp "$MAIN_APP/.env.example" "$CLIENT_DIR/.env"
sed -i "s/DB_DATABASE=laravel/DB_DATABASE=client_$CLIENT_NAME/g" "$CLIENT_DIR/.env"
sed -i "s/APP_URL=http:\/\/localhost/APP_URL=http:\/\/$CLIENT_NAME.application.com/g" "$CLIENT_DIR/.env"

# 软链主应用文件
for item in "$MAIN_APP"/*; do
    item_name=$(basename "$item")
    if [ "$item_name" != ".env" ]; then
        ln -s "$item" "$CLIENT_DIR/$item_name"
    fi
done

# 设置web服务器权限(适配www-data用户)
chown -R www-data:www-data "$CLIENT_DIR"
chmod -R 755 "$CLIENT_DIR/storage" "$CLIENT_DIR/bootstrap/cache"

echo "客户$CLIENT_NAME创建完成!"

给脚本添加执行权限后,即可一键创建客户:

chmod +x create_client.sh
./create_client.sh clientB

4. 配置Nginx子域名指向客户目录

接下来配置Nginx,让clientA.application.com指向客户目录的public文件夹(该public是软链到主应用的public目录):

server {
    listen 80;
    server_name clientA.application.com;
    root /var/www/clients/clientA/public;

    # 安全头部配置
    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-Content-Type-Options "nosniff";

    index index.php;
    charset utf-8;

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

    # 静态文件优化
    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    error_page 404 /index.php;

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php7.4-fpm.sock; # 根据你的PHP版本调整
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
        # 确保Laravel以客户目录为基础路径
        fastcgi_param APP_BASE_PATH /var/www/clients/clientA;
    }

    # 禁止访问隐藏文件(well-known目录除外)
    location ~ /\.(?!well-known).* {
        deny all;
    }
}

配置完成后重启Nginx:

sudo systemctl restart nginx

5. 验证配置有效性

你可以在主应用中添加一个测试路由,验证Laravel是否加载了正确的.env:

// routes/web.php
Route::get('/env-test', function () {
    return [
        'APP_NAME' => env('APP_NAME'),
        'DB_DATABASE' => env('DB_DATABASE'),
        'Base Path' => base_path()
    ];
});

访问clientA.application.com/env-test,就能看到客户A的专属配置信息,确认.env加载正常。

注意事项
  • 依赖更新:主应用执行composer install或composer update后,所有客户的vendor目录(软链到主应用)会自动同步更新,无需单独操作。
  • 配置缓存:不要在主应用执行php artisan config:cache,因为缓存会绑定主应用的.env配置。如果需要缓存,要在每个客户目录下单独执行:cd /var/www/clients/clientA && php artisan config:cache。
  • Storage目录(可选):如果需要每个客户独立的存储目录,不要软链主应用的storage,而是在客户目录下创建独立的storage,然后在bootstrap/app.php中修改Laravel的存储路径(需修改主应用代码,根据需求决定)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:42:22