【.htaccess】URL重写、静态资源缓存、安全防护等

在 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. 测试验收

  1. 把整个 myproject 文件夹放到你的 Apache 文档根目录(例如 htdocs)。

  2. 浏览器访问:

    • 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 处理,实际效果是首页。

  3. F12 打开开发者工具 → 网络(Network),刷新并查看 style.cssapp.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

注意事项

  1. 如果你的子项目不止 myapp1myapp4,记得把列表更新到 RewriteCond 里,用 | 分隔即可。

  2. 404.php 必须放在网站根目录 (和这个 .htaccess 同级),且内容确保没有任何语法错误。

  3. 如果哪天你想把"不存在的路径"都直接丢给 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/ 时:

  1. Apache 看到请求的是目录 myapp4/

  2. 由于 myapp4/.htaccess 的重写条件是 !-d(不是目录),所以重写规则不触发

  3. 但是 DirectoryIndex admin.php 生效,Apache 会自动将请求交给 admin.php 处理。

  4. admin.php 运行,此时 $_SERVER['REQUEST_URI'] 一般是 /myapp4//myapp4

  5. 你的路由代码很可能像这样:

    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.phpdefault 分支确实被触发了。

所以,你只需要让 myapp4 也被识别为首页,问题就解决。


总结 :根目录 301 正常,myapp4 首页 404 是因为路径解析出了 myapp4,而路由没有匹配。在 admin.php 路由开关里加上 case 'myapp4': 即可。

对,就是这么简单。你已经把 myapp4 加进了首页路由,跳转后就能正常显示,而不是被误判为 404。

现在可以再验证一下全流程:

  1. 访问 http://example.com/ → 自动跳到 /myapp4/,地址栏变了,页面是你的首页。

  2. 直接在地址栏输入 /myapp4/home/myapp4/about 等 → 正常走 admin.php 路由,显示对应页面。

  3. 输入 /myapp1//myapp2/ 等 → 各自子项目独立运行,互不干扰。

  4. 输入一个不存在的路径(比如 /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 的处理过程是这样的:

  1. 请求 URI 是 /myapp4/home

  2. Apache 进入 myapp4 目录,发现里面有 .htaccess,启用该文件内的规则。

  3. 此时,重写规则匹配的路径已经去掉了目录前缀 myapp4/ ,只针对当前目录的"相对路径"进行匹配。所以 ^(.*)$ 捕获到的 $1home,而不是 myapp4/home

  4. 于是请求被内部重写为 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,所以必须用查询字符串方式才能正确提取。


补充检查(如果修改后还有问题)

  1. 确保 myapp4/.htaccess 内有且只保留稳定版规则(不要被多余东西干扰):

    apache

    复制代码
    DirectoryIndex admin.php
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^(.*)$ admin.php?/$1 [QSA,L]
  2. 确认 myapp4/admin.php 存在且路径大小写正确(Linux 严格区分大小写)。

  3. 如果其他子项目也使用相同的路由方式,记得把这份 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.php404.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="/子目录名/"> 即可。

相关推荐
TO_WebNow1 小时前
创建webman步骤
php·webman
BingoGo1 小时前
PHP 应用 security.txt 漏洞披露实践
php
JaguarJack1 小时前
PHP 应用 security.txt 漏洞披露实践
后端·php
酉鬼女又兒2 小时前
零基础入门计算机网络:定义、分类与核心性能指标
开发语言·计算机网络·考研·青少年编程·职场和发展·php
isyangli_blog12 小时前
OpenDayLight (Carbon 版本) 启动与组件安装
开发语言·php
代龙涛15 小时前
WordPress page.php 页面模板与自定义模板使用方法
android·开发语言·php
心怀梦想的咸鱼17 小时前
OpenCode 接入 API 报错 read ECONNRESET:基于环境变量的证书校验绕过方案
开发语言·php
云云只是个程序马喽19 小时前
海外短剧系统开发_云微传媒:多语言短剧平台定制与变现解决方案
java·php
24zhgjx-fuhao21 小时前
虚链路的配置
开发语言·网络·php