如何更改我的 mod_rewrite 规则以在父目录中工作?

How can I change my mod_rewrite rules to work in the parent directory?

我有一个 MVC 应用程序 index.php 位于 /public 子文件夹中。目前,我将 Apache 指向 /public 文件夹并在其中使用 .htaccess 将 MVC 请求重写为 index.php.

当前的 Apache 配置:

<VirtualHost>
  DocumentRoot /var/www/html/public
</VirtualHost>

/public中的当前 .htaccess:

RewriteEngine On

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule ^(.*)$ index.php? [L,QSA]

目标 Apache 配置:

<VirtualHost>
  DocumentRoot /var/www/html
</VirtualHost>

目标 .htaccess,但我无法正确重写 MVC 和资产 URL:

DirectoryIndex index.php

RewriteEngine On
RewriteBase /public

# If it's a file/directory/symlink, serve it.
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d [OR]
RewriteCond %{REQUEST_FILENAME} -l
RewriteRule ^.*$ - [NC,L]

# Otherwise let's go MVC
RewriteRule ^.*$ index.php [NC,L]

示例网址:

domain.com/login (MVC URL, rewritten to public/index.php)
domain.com/assets/styles.css (non-MVC URL, rewritten to public/assets folder)

除非您正在设置本地开发环境来模拟限制性更强的实时服务器环境,否则...

Not sure why you want to do this, as your current config (setting the DocumentRoot to /public) would seem to be the most desirable (and simple) setup.

Setting the DocumentRoot to the parent directory and rewriting to the /public subdirectory instead is often a workaround when you are unable to change the DocumentRoot. This is ultimately more work and exposes the /public subdirectory, which now has to be blocked/redirected to prevent direct access.

无论如何,移动到 public HTML space...

中的 /public 子目录

DocumentRoot 设置为 /var/www/html 后,有两种方法可以完成此操作,使用 1 个或 2 个 .htaccess 文件。 (虽然,这也引发了另一个问题......当您有权访问服务器配置时,为什么要使用 .htaccess?尽管这可能有完全正当的理由。)

方法#1 - 两个 .htaccess 文件

使用两个 .htaccess 文件可以说更容易做到这一点。一个在文档根目录中,将所有请求重写到 /public 子目录中,另一个在 /public 子目录中,将请求重写到您的 MVC 应用程序中,其方式与您已经在做的大致相同。 /public/.htaccess 文件的存在也用于防止重写循环。

例如:

# (1) /.htaccess

RewriteEngine On

# (OPTIONAL) If you need to allow access to files/directories/symlinks
#            outside of the "public" directory
#RewriteCond %{REQUEST_FILENAME} -f [OR]
#RewriteCond %{REQUEST_FILENAME} -d [OR]
#RewriteCond %{REQUEST_FILENAME} -l
#RewriteRule ^ - [L]

# Rewrite all requests to the "/public" subdirectory
RewriteRule (.*) public/ [L]
# (2) /public/.htaccess

RewriteEngine On

# If "public" directory is requested directly then redirect to remove it
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule (.*) / [R=301,L]

# Stop early if already rewritten to MVC
RewriteRule ^index\.php$ - [L]

# Rewrite to MVC app, otherwise serve assets
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule (.*) index.php? [QSA,L]

方法#2 - 文档根目录中的一个 .htaccess 文件

或者,您在文档根目录中只有一个 .htaccess 文件。

例如:

# /.htaccess (only)

RewriteEngine On

# If "public" directory is requested directly then redirect to remove it
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^public(?:/(.*))?$ / [R=301,L]

# Stop early if already rewritten to "public" directory
RewriteRule ^public/ - [L]

# If it's a file/directory/symlink, serve it.
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d [OR]
RewriteCond %{REQUEST_FILENAME} -l
RewriteRule ^ - [L]

# Rewrite to assets in "public" directory if they exist
RewriteCond %{REQUEST_URI} \.\w{2,4}$
RewriteCond %{DOCUMENT_ROOT}/public%{REQUEST_URI} -f
RewriteRule !^public/ public%{REQUEST_URI} [L]

# Otherwise rewrite request to the MVC app in "public" directory
RewriteRule (.*) public/index.php? [QSA,L]

我假设您所有的“资产”的文件扩展名都在 2 到 4 个字符之间。对上述内容的优化是,如果您的所有“资产”都被 /assets/... 引用,那么您可以简单地将所有这些请求重写为 /public/assets/... - 无需文件系统检查。例如:

# Rewrite to assets in "public" directory (unconditionally)
RewriteRule ^assets/ public%{REQUEST_URI} [L]

尽管这意味着您不能在 MVC 应用程序中包含以 /assets/ 开头的路由。但你可能不应该这样做。

旁白: 如果您不直接提供目录服务(现在这样做很不常见),那么您不需要检查请求是否映射到目录重写规则。只需让它传递到您的 MVC 应用程序并触发 404(而不是 403)- 这可能更可取。