grav cms 依赖 .htaccess 重写规则处理无后缀 url,xampp 默认禁用目录级重写且未启用 virtualhost 时请求全走默认站点,导致 403/500/404;必须取消 httpd-vhosts.conf 注释、启用 mod_rewrite、配置含 allowoverride all 的 virtualhost 块、绑定自定义域名并映射 hosts。

Grav CMS 在 XAMPP 下无法直接通过多端口访问,必须配合虚拟主机(VirtualHost)配置 + hosts 映射才能正常运行 —— 单纯改 Listen 端口会导致 403、500 或静态文件 404。
为什么不能只改 Listen 端口就跑 Grav?
Grav 依赖 Apache 的 .htaccess 重写规则(如 mod_rewrite)来处理无后缀 URL(/blog 而非 /blog.php)。XAMPP 默认禁用目录级重写,且 VirtualHost 块未启用时,所有请求都走默认站点(htdocs),而 Grav 的 user/ 目录结构与默认权限策略冲突。
常见现象包括:
- 访问
http://localhost:8080显示 “Forbidden” 或空白页 - 首页能打开,但点击任何链接跳转到
http://localhost/blog(端口丢失) - CSS/JS 加载 404,因为重写失败导致路径解析错误
必须启用 httpd-vhosts.conf 并配置 VirtualHost
默认 XAMPP 的 httpd.conf 中 #Include conf/extra/httpd-vhosts.conf 是被注释的,这步漏掉,后续所有 VirtualHost 配置都无效。
操作步骤:
- 用管理员权限打开
D:\xampp\apache\conf\httpd.conf - 搜索
httpd-vhosts.conf,删掉该行开头的#号 - 确认
mod_rewrite已启用:查找LoadModule rewrite_module modules/mod_rewrite.so,确保前面没# - 重启 Apache(XAMPP 控制面板点 Stop → Start)
Grav 项目对应的 VirtualHost 配置要点
编辑 D:\xampp\apache\conf\extra\httpd-vhosts.conf,为 Grav 添加独立块(不要复用 *:80,避免和默认站点冲突):
XAMPP 8.0.30 是一款免费、开源的跨平台 Web 服务器集成包,专为快速搭建本地 PHP 开发环境而设计。该版本核心组件包括:Apache 2.4.56、MySQL 8.0.33、PHP 8.0.30、phpMyAdmin 5.2.1 等。它支持 Windows、Linux 和 macOS 系统,可让开发者在个人电脑上轻松模拟服务器环境,无需复杂配置即可运行 WordPress、Thin
<virtualhost>
ServerName grav.local
DocumentRoot "D:/xampp/htdocs/grav"
<directory>
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
</directory></virtualhost>
关键细节:
-
DocumentRoot必须是 Grav 解压后的**根目录**(含index.php、user/、system/),不是子目录 -
AllowOverride All是重写的前提,缺它.htaccess不生效 -
ServerName不能写localhost,否则浏览器会忽略端口直连 80;必须用自定义域名(如grav.local) - 端口号(如
8080)需在httpd.conf中已声明Listen 8080
hosts 文件与浏览器访问方式
Windows 不识别 grav.local 这类域名,必须手动映射:
- 用管理员权限打开
C:\Windows\System32\drivers\etc\hosts - 末尾添加:
127.0.0.1 grav.local - 保存后无需重启系统,但建议清浏览器 DNS 缓存或用无痕窗口测试
最终访问地址是:http://grav.local:8080(不是 localhost:8080)
如果多个 Grav 实例共存,每个都要配独立域名(grav-dev.local、grav-test.local)+ 独立端口 + 独立 DocumentRoot,否则路由和缓存会互相污染。
Grav 的 system/config/system.yaml 中 absolute_urls: false 是默认值,别改成 true —— 否则生成的链接会硬编码成 http://localhost,绕过你配的端口和域名。










