在 Apache 服务器环境中,.htaccess("Hypertext Access" 的缩写)是一个分布式配置文件 。它允许你在不修改服务器主配置文件(httpd.conf)的情况下,对当前目录及其所有子目录施加行为控制。
在 PHP 项目中,它的主要用途可以归纳为以下几类:
1. URL 重写(最核心的用途)
绝大多数现代 PHP 框架(Laravel、ThinkPHP、Symfony 等)都依赖它实现单一入口 和美化 URL。
-
隐藏入口文件 :把
index.php/controller/action变成/controller/action -
伪静态 :将
.html结尾的 URL 实际交给 PHP 处理
2. 重定向与跳转
-
强制 HTTP → HTTPS
-
强制带
www或不带www的域名统一 -
网站改版时做 301 永久重定向
示例(强制 HTTPS):
apache
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]
3. 安全与访问控制
-
禁止直接访问敏感文件 :比如
.env、数据库配置、日志文件。 -
IP 黑白名单:只允许特定 IP 访问后台或开发工具。
-
目录浏览保护:防止用户看到文件列表。
-
HTTP 认证:给某些目录加弹出密码。
示例(禁止访问 .env):
4. 自定义错误页面
让访问者看到你设计的 404 / 500 页面,而不是服务器默认的白屏。
apache
复制
下载
ErrorDocument 404 /errors/404.html
ErrorDocument 500 /errors/500.php
5. 性能优化
-
启用 Gzip 压缩:压缩 HTML、CSS、JS 等文本内容,减小传输体积。
-
设置缓存头(Expires / Cache-Control):让浏览器缓存静态资源,加快二次访问速度。
-
禁用图片防盗链。
6. 调整 PHP 配置(视运行模式而定)
仅在 PHP 作为 Apache 模块(mod_php)运行时有效。如果是 PHP-FPM 或 FastCGI 模式,这些指令通常会报 500 错误。
4. 部署前必须检查的 Apache 配置
在 Windows 的 Apache(XAMPP/Wamp/Laragon 等)里,确保以下操作已完成:
① 开启必要模块
打开 httpd.conf(通常在 apache/conf/ 下),取消下面这些行的注释(去掉前面的 #):
apache
复制
下载
LoadModule rewrite_module modules/mod_rewrite.so
LoadModule deflate_module modules/mod_deflate.so
LoadModule expires_module modules/mod_expires.so
LoadModule headers_module modules/mod_headers.so # 虽然案例没用到,但建议开
② 允许 .htaccess 覆盖权限
在 httpd.conf 中,找到你网站对应的 <Directory> 配置(或全局 AllowOverride 设置),确保是 All:
apache
复制
下载
<Directory "C:/xampp/htdocs">
AllowOverride All
# ...
</Directory>
修改后重启 Apache。

5. 测试验收
-
把整个
myproject文件夹放到你的 Apache 文档根目录(例如htdocs)。 -
浏览器访问:
-
http://localhost/myproject/home→ 看到首页 -
http://localhost/myproject/about→ 看到关于页 -
http://localhost/myproject/.env→ 看到 403 Forbidden 或被拒绝访问 -
http://localhost/myproject/abc→ 看到自定义 404 页面 -
尝试直接访问
http://localhost/myproject/如果目录列表没有开启,会显示首页(因为根路径映射到 home),或者也可能被重写到 index.php 处理,实际效果是首页。
-
-
按
F12打开开发者工具 → 网络(Network),刷新并查看style.css或app.js的响应头,你会看到:-
Content-Encoding: gzip(压缩生效) -
Cache-Control: max-age=...或Expires: ...(缓存生效)
-
如果某个功能不生效,请检查对应模块是否已启用、.htaccess 文件名是否正确(没有额外后缀)、文件编码是否为 UTF-8 无 BOM。
- 如果你的 Apache 没有配置默认索引文件找到
index.php,访问根路径可能需要手动加/home,或者你可以在.htaccess里添加DirectoryIndex index.php。

这几行是 PHP 项目实现「单一入口」和「美化 URL」最经典的核心配置。下面逐行拆解,并告诉你以后如果需求变了,应该怎么改、为什么这样改。
一、逐行详解
apache
RewriteEngine On
开启重写引擎。没有这一行,后面所有重写规则全部无效。
apache
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
这两行是重写的条件 ,必须同时满足才会执行下一条 RewriteRule:
-
%{REQUEST_FILENAME}:当前请求在服务器上对应的物理文件路径 。比如你访问
https://aliw.cn/home,Apache 会拼出类似/home/用户名/public_html/home这样的路径。 -
!-f:不是一个真实存在的文件。 -
!-d:不是一个真实存在的目录。
为什么要加这两个条件?
如果你访问的是一个真实存在的图片、CSS、JS 或某个目录,就不要重写给 PHP,直接让 Apache 返回真实文件。否则,连静态资源都会走 index.php,不仅浪费资源,还可能显示乱码。
apache
RewriteRule ^(.*)$ index.php?/$1 [QSA,L]
这是重写规则本身。
-
^(.*)$:一个正则表达式,匹配请求 URI 的路径部分(去掉域名和查询字符串后剩下的)。-
^表示行首,$表示行尾,(.*)捕获所有字符。 -
例如
/home会捕获到home,/about/team会捕获到about/team。
-
-
index.php?/$1:重写目标。-
$1代表前面(.*)捕获到的内容。 -
你访问
/home,实际服务器内部处理为index.php?/home。 -
这里故意加了一个
?,将路径作为查询字符串的一部分传递,而不是使用index.php/home的形式。
-
-
[QSA,L]:标志位。-
QSA(Query String Append):如果原始请求带了查询参数(如?page=2),会附加到新 URL 后面,而不是被丢弃。 -
L(Last):如果这条规则匹配成功,停止执行后续的重写规则。
-
二、为什么要用 index.php?/$1 而不是 index.php/$1?
你的线上环境正是因为这个差异,才从 500 错误变成正常。
-
index.php/$1依赖 Apache 的 PATH_INFO 功能,会将/home作为PATH_INFO变量传给 PHP。但不少虚拟主机或 PHP-FPM 模式下,PATH_INFO 可能被禁用或处理异常,导致 500 错误。 -
index.php?/$1把路径伪装成了查询字符串的一部分 (非常巧妙的 trick)。在 PHP 的
$_SERVER['REQUEST_URI']中仍然能正确获取到/home,但服务器只把它当作普通的 GET 参数处理,完全不依赖 PATH_INFO,兼容性极好。
因此,这个 ? 是你线上稳定的关键,不要随意去掉。
三、常见修改场景及对应的改法
理解了上面每一部分的作用,以后你想改功能就能对症下药了。
1. 想把入口文件从 index.php 改成 admin.php
将重写目标改为:
apache
RewriteRule ^(.*)$ admin.php?/$1 [QSA,L]
逻辑不变,只是换了一个 PHP 文件来处理。
2. 项目不在网站根目录,而在子目录(如 myapp)
如果你把项目放在 public_html/myapp/,访问时是 https://aliw.cn/myapp/home,那么需要让重写路径相对 于当前目录。可以在规则前加上 RewriteBase:
apache
RewriteEngine On
RewriteBase /myapp/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?/$1 [QSA,L]
这样 $1 就不会包含 myapp 前缀,路由解析更干净。
3. 排除某些文件类型不做重写(保护敏感文件)
比如你想禁止访问所有 .env、.log 文件,并直接返回 403,可以在条件前面加:
apache
RewriteRule \.env$ - [R=403,L]
RewriteRule \.log$ - [R=403,L]
- 表示"不做任何替换,直接按标志处理",这里给了 403 状态码并停止。
4. 强制全站 HTTPS
在 RewriteEngine On 下面增加:
apache
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]
注意:若服务器前面有 CDN,可能需要用 %{HTTP:X-Forwarded-Proto} 而非 %{HTTPS},否则会死循环(之前线上 500 可能也与此有关)。
5. 移除 QSA,避免参数叠加
有时候你希望完全控制新的查询参数,不希望原始参数保留,就去掉 QSA:
apache
RewriteRule ^(.*)$ index.php?/$1 [L]
这样原始请求 ?page=2 会被丢弃。通常保留 QSA 更合理。
四、修改逻辑总结
当你需要调整路由行为时,按下面的逻辑改对应部位:
| 需求 | 修改点 |
|---|---|
| 换一个入口文件 | 改 RewriteRule 目标文件 |
| 项目在子目录 | 加 RewriteBase |
| 排除某些路径不重写 | 在 RewriteRule 前加带 - 的拦截规则 |
| 添加或去掉查询字符串 | 使用或移除 QSA 标志 |
| 强制 HTTPS | 增加 HTTPS 判断规则(注意代理) |
| 让重写停止 | 保留 L 标志,后续规则不执行 |
| 解决 PATH_INFO 报错 | 使用 index.php?/$1,确保那个 ? 存在 |
方案一:访问根域名自动跳转到 /myapp4/(推荐,最清晰)
在根目录的 .htaccess 中写入:
apache
RewriteEngine On
# 只对根路径进行 301 重定向
RewriteRule ^$ /myapp4/ [R=301,L]
效果:
-
访问
http://example.com/→ 浏览器地址栏变成http://example.com/myapp4/,然后由myapp4/.htaccess内的规则(DirectoryIndex admin.php)接管,正常显示首页。 -
访问
http://example.com/myapp1/等,不受影响,各自独立。
优点 :
路径清晰,静态资源路径无需任何调整,所有链接自动基于 /myapp4/。
在方案一中(只重定向根路径)
方案一的 .htaccess 只有一行跳转:
apache
RewriteRule ^$ /myapp4/ [R=301,L]
这条规则只匹配完全为空的请求路径 (就是访问 http://example.com/ 的时候)。
对于任何其他路径(比如 /about、/abc、/myapp1/),这条规则根本不会触发,Apache 会按照默认方式处理:
-
如果路径是一个真实存在的文件或目录 → 正常返回文件或进入目录索引。
-
如果路径不存在 ,而且根目录的 .htaccess 里没有别的重写规则 → 返回 404 Not Found (由主机默认的 404 页面或主配置处理)。
-
它不会自动交给 myapp4 处理,也不会尝试去子目录里找。
所以,方案一非常干净:根域名一跳转,其他路径各安天命,子项目互不干扰。
如果你希望不存在的路径也由 myapp4 接管
那才需要方案二 的思路,也就是你问题里写的那些复杂条件。那时访问 /about(不存在)会被内部重写到 myapp4/admin.php?/about,让 myapp4 的路由来决定显示什么(可能是首页,也可能是它自己的 404)。
但如之前所说,这样会面临静态资源路径错乱的问题,需要配合 <base> 标签或全路径。
一句话总结
方案一:
-
访问根域名 → 跳到
/myapp4/。 -
访问
/abc(不存在) → 直接 404,和 myapp4 无关。 -
访问
/myapp1/→ 进入 myapp1 子项目。 -
不需要处理你问题里的"不以 myapp1/2/3/4 开头"的情况,因为根本没这些判断。
如果你实际想要的是"不存在的路径自动转给 myapp4",那才需要走方案二,并解决路径问题。
你只需要在根目录的 .htaccess 里增加几条规则,就可以让"不存在的路径"显示你自己的错误页面,而不会回到主机商默认的白屏 404。
因为根目录你只做了 RewriteRule ^$ /myapp4/,其他路径本来要么存在,要么触发 Apache 默认 404。现在我们要接管这些"不存在路径",同时不能误伤 myapp1 ~ myapp4 这些子项目,也不能影响真实存在的文件和目录。
最终根目录 .htaccess(方案一增强版)
apache
RewriteEngine On
# 1. 访问根路径时,301 跳转到 myapp4
RewriteRule ^$ /myapp4/ [R=301,L]
# 2. 对于不存在的路径(且不是子项目),显示自定义 404 页面
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !^/(myapp1|myapp2|myapp3|myapp4)/
RewriteRule ^(.*)$ /404.php [QSA,L]
逐条解释:
-
RewriteCond %{REQUEST_FILENAME} !-f→ 不是真实文件 -
RewriteCond %{REQUEST_FILENAME} !-d→ 不是真实目录 -
RewriteCond %{REQUEST_URI} !^/(myapp1|myapp2|myapp3|myapp4)/→ 请求路径不是 以/myapp1/、/myapp2/等开头(保护子项目) -
RewriteRule ^(.*)$ /404.php [QSA,L]→ 满足条件时,内部重写到根目录下的404.php,不会改变浏览器地址栏(看不到/404.php)
你需要做的:新建一个 404.php 放在网站根目录
内容如下:
<?php
http_response_code(404);
?>
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>404 - 页面未找到</title>
<style>
body { font-family: sans-serif; padding: 50px; text-align: center; }
h1 { font-size: 2em; color: #c00; }
</style>
</head>
<body>
<h1>404</h1>
<p>抱歉,您访问的页面不存在。</p>
<p><a href="/myapp4/">返回首页</a></p>
</body>
</html>
这样做之后,各种访问的结果是:
| 访问的 URL | 结果 |
|---|---|
http://example.com/ |
301 跳转到 /myapp4/ |
http://example.com/myapp4/ |
正常进入 myapp4 首页 |
http://example.com/myapp4/home |
正常进入 myapp4 的 home 路由 |
http://example.com/myapp1/ |
正常进入 myapp1 子项目 |
http://example.com/existing-file.txt |
返回真实文件(不触发 404) |
http://example.com/abc |
显示你自定义的 404 页面,状态码 404 |
http://example.com/myapp5/ |
因为 myapp5 不在保护列表里,如果目录不存在且也不是文件,就会走 404 |
注意事项
-
如果你的子项目不止
myapp1~myapp4,记得把列表更新到RewriteCond里,用|分隔即可。 -
404.php必须放在网站根目录 (和这个.htaccess同级),且内容确保没有任何语法错误。 -
如果哪天你想把"不存在的路径"都直接丢给
myapp4的统一路由处理,那就需要回到方案二,但需要处理静态资源路径问题。目前这样各项目独立,且统一了 404 外观,已经是最稳妥的做法。
你现在可以直接用这个增强版 .htaccess 替换根目录原有的,上传后访问一个随便的 /test404,就能看到自己设计的错误页了。
访问 http://example.com/ → 301 跳转到 /myapp4/,但 /myapp4/ 却显示了"页面被吃掉了(404)",说明跳转本身成功了,但是 myapp4 子项目没能正确输出首页 。
这个问题跟根目录的 .htaccess 无关,跟 myapp4 自身的 .htaccess 和入口文件 admin.php 的路由逻辑有关。
1. 先快速确认两个基本点
-
myapp4/目录下确实存在admin.php这个文件。 -
myapp4/.htaccess确实包含DirectoryIndex admin.php(建议放在文件顶部)。
如果这两点都满足,还是 404,那就是路由没处理空路径的问题。
2. 为什么会出现"页面被吃掉了"?
当浏览器访问 /myapp4/ 时:
-
Apache 看到请求的是目录
myapp4/。 -
由于
myapp4/.htaccess的重写条件是!-d(不是目录),所以重写规则不触发。 -
但是
DirectoryIndex admin.php生效,Apache 会自动将请求交给admin.php处理。 -
admin.php运行,此时$_SERVER['REQUEST_URI']一般是/myapp4/或/myapp4。 -
你的路由代码很可能像这样:
path = parse_url(request_uri, PHP_URL_PATH);
path = trim(path, '/');
/myapp4/ 去掉首尾斜杠后就变成了 myapp4,而不是空字符串。
如果你的 switch 里只匹配了 '' 和 'home',那 myapp4 会掉进 default 分支,于是显示 404。
这就是跳转后看到 404 的根源。
3. 解决办法
方法一:修改 admin.php 的路由逻辑(推荐)
在 admin.php 里,把子目录名称也视为首页路由。比如:
switch ($path) {
case '':
case 'myapp4': // 这一行新增
case 'home':
require __DIR__ . '/pages/home.php';
break;
case 'about':
require __DIR__ . '/pages/about.php';
break;
default:
http_response_code(404);
require __DIR__ . '/errors/404.html';
break;
}
这样,/myapp4/ 会被正确识别为首页,显示正常内容。
方法二:调整重写规则,让目录请求也走重写
在 myapp4/.htaccess 里,把 !-d 条件去掉(或者用其他方式),使根目录请求也触发重写。但不建议,因为这会破坏对真实静态资源的正常访问。更安全的方式是用方法一。
4. 如何验证和测试
-
直接访问
http://example.com/myapp4/admin.php,如果能正常显示首页,则说明文件存在且可执行。 -
访问
http://example.com/myapp4/,看是否还 404。改完admin.php后刷新即可。
5. 额外提醒
你的自定义 404 页面里可能有"页面被吃掉了"的文案。现在 /myapp4/ 显示的就是这个,说明 admin.php 的 default 分支确实被触发了。
所以,你只需要让 myapp4 也被识别为首页,问题就解决。
总结 :根目录 301 正常,myapp4 首页 404 是因为路径解析出了 myapp4,而路由没有匹配。在 admin.php 路由开关里加上 case 'myapp4': 即可。
对,就是这么简单。你已经把 myapp4 加进了首页路由,跳转后就能正常显示,而不是被误判为 404。
现在可以再验证一下全流程:
-
访问
http://example.com/→ 自动跳到/myapp4/,地址栏变了,页面是你的首页。 -
直接在地址栏输入
/myapp4/home、/myapp4/about等 → 正常走admin.php路由,显示对应页面。 -
输入
/myapp1/、/myapp2/等 → 各自子项目独立运行,互不干扰。 -
输入一个不存在的路径(比如
/abc)→ 显示你在根目录404.php里设计的错误页面。
如果以后你把其他子项目(比如 myapp1)的入口也改成了 admin.php,并且想支持直接访问 http://example.com/myapp1/,也别忘了在各自的 admin.php 里加上 case 'myapp1':,或者在各自的 .htaccess 里加上 DirectoryIndex admin.php,再配合路由处理空路径即可。
现在整个结构清晰、安全,也预留了自定义错误页,可以放心用了。
不需要调整其他 case ,比如 case 'home':、case 'about':,它们继续正常工作,一点也不用改。
为什么不用改?
关键在于你的 myapp4/.htaccess 里的重写规则,它是写在子目录内的:
apache
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ admin.php?/$1 [QSA,L]
当你在浏览器访问 http://example.com/myapp4/home 时,Apache 的处理过程是这样的:
-
请求 URI 是
/myapp4/home。 -
Apache 进入
myapp4目录,发现里面有.htaccess,启用该文件内的规则。 -
此时,重写规则匹配的路径已经去掉了目录前缀
myapp4/,只针对当前目录的"相对路径"进行匹配。所以^(.*)$捕获到的$1是home,而不是myapp4/home。 -
于是请求被内部重写为
admin.php?/home,$_SERVER['REQUEST_URI']仍然是/myapp4/home(对外不变),但路由脚本拿来做trim时,去掉首尾斜杠后得到的就是home。
所以,你的 switch 里:
-
case 'home':--- 匹配成功 ✅ -
case 'about':--- 匹配成功 ✅ -
所有原有的路由分支,都原封不动继续工作。
唯一多做的就是加了一个 case 'myapp4':,用来处理直接访问子目录根路径 (如 /myapp4/ 或 /myapp4)时,路径解析出来是 myapp4 的特殊情况。这个情况只有当你用 DirectoryIndex 而不走重写时才会出现,所以需要单独处理。
简单总结
-
原有的路由逻辑无需任何调整 ,访问
/myapp4/home、/myapp4/about等一切照旧。 -
添加的
case 'myapp4':仅仅是为了解决目录根路径 被DirectoryIndex导向admin.php时,路径字符串为myapp4的识别问题。
这样,你的 admin.php 结构清晰,以后无论项目放在哪个子目录,只要在文件里加一个与目录名对应的 case 就能兼容根路径访问,其他路由不受任何影响。
明白了!问题根源找到了:你的 .htaccess 重写规则用了 admin.php?/1,路径实际上是作为**查询字符串** 传给 PHP 的,但你的 admin.php 还是按旧方式从 _SERVER['REQUEST_URI'] 直接取路径,没处理掉子目录前缀,所以 /myapp4/home 被解析成了 myapp4/home,当然匹配不到 home。
修复方法:调整 admin.php 的路径获取逻辑
找到你 admin.php 里这段代码(原先的):
php
$request_uri = $_SERVER['REQUEST_URI'] ?? '/';
$path = parse_url($request_uri, PHP_URL_PATH);
$path = trim($path, '/');
替换为下面这段,优先从查询字符串读取路径:
php
// 1. 优先从查询字符串中获取路径(适配 admin.php?/... 模式)
if (isset($_SERVER['QUERY_STRING']) && $_SERVER['QUERY_STRING'] !== '' && $_SERVER['QUERY_STRING'][0] === '/') {
// 查询字符串以 / 开头,说明是重写规则传过来的路径
$path = trim($_SERVER['QUERY_STRING'], '/');
} else {
// 2. 否则从 REQUEST_URI 中获取(兼容直接访问、DirectoryIndex 等情况)
$request_uri = $_SERVER['REQUEST_URI'] ?? '/';
$path = parse_url($request_uri, PHP_URL_PATH);
// 去掉最前面的 /,以及如果有子目录前缀,也会保留在 path 里(如 myapp4)
$path = trim($path, '/');
}
这样修改后:
-
访问
/myapp4/home→ 查询字符串是/home→$path = 'home'→ ✅ 匹配case 'home': -
访问
/myapp4/about→ 查询字符串是/about→$path = 'about'→ ✅ 匹配 -
访问
/myapp4/→ 没有查询字符串,走else分支,$path = 'myapp4'→ ✅ 匹配case 'myapp4':(之前加的) -
访问
/myapp4/abc→ 查询字符串是/abc→$path = 'abc'→ 进入default→ 显示 404
为什么之前 https://aliw.cn/home 可以,子目录就不行?
-
之前线上测试时,项目直接在根目录 ,访问
/home,$_SERVER['QUERY_STRING']是/home,但走else分支时$path也是home,两种方式结果相同。 -
现在项目在子目录
myapp4,用else分支时$path会是myapp4/home,所以必须用查询字符串方式才能正确提取。
补充检查(如果修改后还有问题)
-
确保
myapp4/.htaccess内有且只保留稳定版规则(不要被多余东西干扰):apache
DirectoryIndex admin.php RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ admin.php?/$1 [QSA,L] -
确认
myapp4/admin.php存在且路径大小写正确(Linux 严格区分大小写)。 -
如果其他子项目也使用相同的路由方式,记得把这份
admin.php和.htaccess同步修改,并且按需添加各自的case '子目录名':。
现在去改 admin.php,刷新页面就能看到首页了。
需要修改。
因为你现在的项目跑在子目录 /myapp4/ 下,所有以 / 开头的路径都会被浏览器解析为网站根目录 ,而不是 myapp4 目录。
问题分析
当前 home.php 里的链接:
<a href="/about">关于我们</a>
<link rel="stylesheet" href="/assets/css/style.css">
<script src="/assets/js/app.js"></script>
浏览器实际请求的地址是:
-
http://example.com/about -
http://example.com/assets/css/style.css -
http://example.com/assets/js/app.js
但你真正的页面和资源都在 myapp4 下面:
-
http://example.com/myapp4/about -
http://example.com/myapp4/assets/css/style.css -
http://example.com/myapp4/assets/js/app.js
所以点击"关于我们"会跳到 http://example.com/about,而这个路径如果根目录没有对应处理,就会触发你设置的自定义 404 页面(或根目录的默认 404);CSS/JS 也会加载失败,页面样式全无。
三种修改方案,按需选择
方案一:全部改成绝对路径(带子目录前缀)
<a href="/myapp4/about">关于我们</a>
<link rel="stylesheet" href="/myapp4/assets/css/style.css">
<script src="/myapp4/assets/js/app.js"></script>
✅ 简单直接,无论页面在哪个子目录都能正确引用。
❌ 如果以后换目录(比如从 myapp4 改为 app),所有链接都要批量修改。
方案二:使用 标签(推荐)
在 <head> 最前面加入:
<base href="/myapp4/">
然后所有资源链接仍然可以写成:
<a href="about">关于我们</a> <!-- 注意:去掉了前面的 / -->
<link rel="stylesheet" href="assets/css/style.css">
<script src="assets/js/app.js"></script>
✅ 修改量最小,迁移项目只需改一个 <base> 标签。
✅ 路径看起来更干净,像在根目录一样。
⚠️ 注意 :<base> 会影响页面内所有相对路径(包括锚点),使用时务必用目录结尾的斜杠 (/myapp4/),而不是 /myapp4(缺少结尾斜杠可能导致浏览器解析错误)。
方案三:全部改用相对路径
<a href="../myapp4/about">关于我们</a> <!-- 太麻烦,不推荐 -->
<link rel="stylesheet" href="../myapp4/assets/css/style.css">
❌ 不推荐,因为不同页面层级深度不同,维护困难。
我的建议
直接用方案二 ,在你的 home.php(以及所有其他页面如 about.php、404.php)的 <head> 里添加:
<base href="/myapp4/">
然后把原来以 / 开头的链接和资源路径前的 / 去掉,变成相对路径:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<base href="/myapp4/">
<title>首页</title>
<link rel="stylesheet" href="assets/css/style.css">
</head>
<body>
<h1>🏠 欢迎来到首页</h1>
<p><a href="about">关于我们</a></p>
<script src="assets/js/app.js"></script>
</body>
</html>
现在点击"关于我们"会正确跳转到 /myapp4/about,静态资源也会从 /myapp4/assets/ 加载,一切正常。
额外提醒
如果你其他子项目(myapp1、myapp2...)也有类似的前端资源,也需要做同样处理,避免互相干扰。每个子项目用自己对应的 <base href="/子目录名/"> 即可。