我有一个旧版(Tomcat)网站,以前称为 host.domain,最近部署了一个 Drupal 网站。新的 Drupal 服务器现在是 host.domain,旧的 Tomcat 服务器是 legacy.domain。大部分层次结构已在 Drupal 中复制,因此书签和搜索引擎结果应该仍然有效:请求 host.domain/dir/page.jsp,Drupal 将修剪 .jsp 并查找名为 dir/page 的节点。部分内容尚未迁移,因此我使用“Redirects 404”Drupal 模块在返回 404 之前检查旧服务器的内容:请求 host.domain/legacy/oldpage.jsp,Drupal 查找 legacy/oldpage 节点,未找到,尝试 legacy.domain/legacy/oldpage.jsp,找到它,并使用 drupal_http_request() 透明地将内容传递给浏览器 - 客户端的 URL 甚至没有改变。这部分工作得很好。
问题在于 .js 和 .css 文件 - 出于某种原因,对 host.domain/legacy/file.js 或 file.css 的请求不会触发 Drupal 的 404 处理,因此 Drupal 永远不会询问 legacy.domain 是否有该文件。相反,404 会转到 Apache,并显示 Apache 404(不是 Drupal 或 Tomcat)。这意味着,如果在 host.domain 上通过 Drupal 提供的 legacy.domain 上的内容包含本地 css 或 js,则不会获得这些样式或行为
在我看来,我有三个选择:
- 从 Tomcat 服务器复制 css 和 js 文件,并将它们放在 Drupal 根目录中,同时复制旧的目录层次结构。这可能有效,但会很混乱,使 Drupal 核心更新复杂化,并且可能会干扰正在运行的内容代理 404 行为。
- 让 Drupal 为 .js 和 .css 文件触发 404,就像它为 .jsp 文件触发一样。有什么想法为什么它还没有触发吗?
- 如果 Drupal 不会针对 .js 和 .css 文件抛出 404,则让 Apache 充当 Drupal 代理行为的第二层。如果 404 错误传到 Apache,则让其尝试从 legacy.domain 提供服务。
我想我也可以浏览旧 Tomcat 服务器上的所有内容,并使用 legacy.domain 名称将所有相对包含替换为绝对包含,但我已经在尝试移动该内容离开那个主机和我真的不想在即将被替换的文件上花费精力——我只希望它们在迁移之前能够正常工作。有人对实施选项 2 或 3 有什么建议或教程吗?
Apache 配置是 Ubuntu 12.04.3 LTS。Drupal 目录中的 .htaccess 是:
# Protect files and directories from prying eyes.
<FilesMatch "\.(engine|inc|info|install|make|module|profile|test|po|sh|.*sql|theme|tpl(\.php
)?|xtmpl)(~|\.sw[op]|\.bak|\.orig|\.save)?$|^(\..*|Entries.*|Repository|Root|Tag|Template)$|
^#.*#$|\.php(~|\.sw[op]|\.bak|\.orig\.save)$">
Order allow,deny
</FilesMatch>
# Don't show directory listings for URLs which map to a directory.
Options -Indexes
# Follow symbolic links in this directory.
Options +FollowSymLinks
# Make Drupal handle any 404 errors.
ErrorDocument 404 /index.php
# Set the default handler.
DirectoryIndex index.php index.html index.htm
# Override PHP settings that cannot be changed at runtime. See
# sites/default/default.settings.php and drupal_environment_initialize() in
# includes/bootstrap.inc for settings that can be changed at runtime.
# PHP 5, Apache 1 and 2.
<IfModule mod_php5.c>
php_flag magic_quotes_gpc off
php_flag magic_quotes_sybase off
php_flag register_globals off
php_flag session.auto_start off
php_value mbstring.http_input pass
php_value mbstring.http_output pass
php_flag mbstring.encoding_translation off
</IfModule>
# Requires mod_expires to be enabled.
<IfModule mod_expires.c>
# Enable expirations.
ExpiresActive On
# Cache all files for 2 weeks after access (A).
ExpiresDefault A1209600
<FilesMatch \.php$>
# Do not allow PHP scripts to be cached unless they explicitly send cache
# headers themselves. Otherwise all scripts would have to overwrite the
# headers set by mod_expires if they want another caching behavior. This may
# fail if an error occurs early in the bootstrap process, and it may cause
# problems if a non-Drupal PHP file is installed in a subdirectory.
ExpiresActive Off
</FilesMatch>
</IfModule>
# Various rewrite rules.
<IfModule mod_rewrite.c>
RewriteEngine on
# This forces all drupal links to end in a trailing slash.
# Companion rules to trailing slash module.
# https://drupal.org/project/trailing_slash
RewriteBase /
RewriteCond %{REQUEST_METHOD} !=post [NC]
RewriteRule ^(.*(?:^|/)[^/\.]+)$ $1/ [L,R=301]
# Set "protossl" to "s" if we were accessed via https://. This is used later
# if you enable "www." stripping or enforcement, in order to ensure that
# you don't bounce between http and https.
RewriteRule ^ - [E=protossl]
RewriteCond %{HTTPS} on
RewriteRule ^ - [E=protossl:s]
# Make sure Authorization HTTP header is available to PHP
# even when running as CGI or FastCGI.
RewriteRule ^ - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
# Block access to "hidden" directories whose names begin with a period.
RewriteRule "(^|/)\." - [F]
# Pass all requests not referring directly to files in the filesystem to
# index.php. Clean URLs are handled in drupal_environment_initialize().
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !=/favicon.ico
RewriteRule ^ index.php [L]
# Rules to correctly serve gzip compressed CSS and JS files.
# Requires both mod_rewrite and mod_headers to be enabled.
<IfModule mod_headers.c>
# Serve gzip compressed CSS files if they exist and the client accepts gzip.
RewriteCond %{HTTP:Accept-encoding} gzip
RewriteCond %{REQUEST_FILENAME}\.gz -s
RewriteRule ^(.*)\.css $1\.css\.gz [QSA]
# Serve gzip compressed JS files if they exist and the client accepts gzip.
RewriteCond %{HTTP:Accept-encoding} gzip
RewriteCond %{REQUEST_FILENAME}\.gz -s
RewriteRule ^(.*)\.js $1\.js\.gz [QSA]
# Serve correct content types, and prevent mod_deflate double gzip.
RewriteRule \.css\.gz$ - [T=text/css,E=no-gzip:1]
RewriteRule \.js\.gz$ - [T=text/javascript,E=no-gzip:1]
<FilesMatch "(\.js\.gz|\.css\.gz)$">
# Serve correct encoding type.
Header set Content-Encoding gzip
# Force proxies to cache gzipped & non-gzipped css/js files separately.
Header append Vary Accept-Encoding
</FilesMatch>
</IfModule>
</IfModule>
更新
根据 Shane Madden 的以下建议,我将其添加到根 .htaccess 的 mod_rewrite 部分的顶部:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} \.(css|js)$
RewriteRule ^(.*)$ http://legacy.domain/$1 [L]
如果我请求 host.domain/oldfile.css,此方法有效:即使 oldfile.css 在旧版主机上不存在,我也会收到 Tomcat 404,因此我知道重写有效。问题在于不存在的目录中不存在的目录。
如果我在旧系统上有一个文件:
http://legacy.domain/root.css
并请求
http://host.domain/root.css
该文件将会显示,因为它满足三个 RewriteCond 规则。但是,如果我要求
http://host.domain/long/path/to/file.css
然后我得到一个 Apache(不是 Tomcat)404,并在 error.log 中有一个条目:
File does not exist: /var/www/long
看起来,重写规则仅在请求的文档与包含该规则的 .htaccess 位于同一目录中时才生效。如果请求的文件位于目录中,则目录触发 404,因为它不以 .css 或 .js 结尾,所以不符合条件,Apache 会立即停止处理。有没有办法让规则适用于任何404,无论它在本地不存在的目录层次结构中有多深?
答案1
由于 drupal 站点的所有文件都应该位于/sites
目录中的真实文件中,因此如何直接代理文件系统中不存在的 css 和 js 文件?
<Directory>
在Drupal 安装块中,类似这样的内容:
RewriteCond %{REQUEST_FILENAME} \.(css|js)$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ http://proxy-target/$1 [P,L]
编辑:
由于文件系统映射在检查是否存在之前就已经阻塞,因此我们可以在不需要文件系统映射的情况下进行检查。
将其直接放入您的<VirtualHost>
块中(如果您不使用虚拟主机,则放入主服务器配置中):
RewriteCond %{REQUEST_URI} \.(css|js)$
RewriteCond /path/to/your/docroot%{REQUEST_URI} !-f
RewriteRule ^/(.*)$ http://proxy-target/$1 [P,L]