apache.htaccessmod-rewrite

htaccess mod_rewrite from old folder to new folder only if file exists in new folder


I have a specific mod_rewrite case which I don't know how to solve or if it is solveable at all.

To start I have the folder foo with many files and subfolders in it, e.g.

foo/subfoo/example.html

I now rename foo to bar. Then I re-create a new empty folder foo for a new project.

But I have many clients that have bookmarked the old URLs. If I want to continue to keep them working, I want to do the following:

  1. Request for foo/subfoo/example.html comes in
  2. Check if bar/subfoo/example.html exists. If yes go there.
  3. If not, continue to foo/subfoo/example.html in the new foo folder which might or might not exist

Anyway to check in RewriteCond if bar/subfoo/example.html exists before rewrite happens (e.g. using regex)?

I have seen examples that check the existence of a file based on the REQUEST_FILENAME first but I want to check the existence of a file in a modified REQUEST_FILENAME.


Solution

  • Assuming you have changed all the old URLs from /foo/... to /bar/... (including all links/URLs to static assets) and this is simply for "clients that have bookmarked the old URLs" (and possibly search engines that have indexed the old URLs) then you can implement a 301 "permanent" external redirect as follows in .htaccess:

    RewriteEngine On
    
    # Redirect requests from "foo" to "bar" if the file exists in "bar"
    RewriteCond %{DOCUMENT_ROOT}/bar/$1 -f
    RewriteRule ^foo/(.+\.(html|css|js|webp|jpg|gif))$ /bar/$1 [R=301,L]
    

    This rule would need to go near the top of the root .htaccess file before existing mod_rewrite directives.

    The $1 backreference contains the requested URL-path, less the /foo/ prefix. This is then used to construct the file-path to test for in the preceding RewriteCond directive and to redirect to in the RewriteRule substitution (if the condition is successful).

    The (html|css|js|webp|jpg|gif) part of the regex checks just for the file extensions we are concerned with. Although, strictly speaking, if this is only for bookmarked/indexed URLs then you may just need the .html extension here.

    You do not need to repeat the RewriteEngine directive if this already exists later in the file.

    You should test first with a 302 (temporary) redirect to avoid potential caching issues if there happens to be an error.

    Note that this does not redirect requests for directories themselves, just files within those directories. So a request for /foo/subfoo/ would not be redirected, but /foo/subfoo/example.html would. An additional rule would be required to handle such requests if that is a requirement.

    Background...

    I have seen examples that check the existence of a file based on the REQUEST_FILENAME first but I want to check the existence of a file in a modified REQUEST_FILENAME

    REQUEST_FILENAME is a server variable that contains the computed absolute filesystem path that the requested URL-path would map to. Most of the time this is the same as DOCUMENT_ROOT + REQUEST_URI (the requested root-relative URL-path).

    RewriteRule TestString CondPattern [flags]

    The first argument to the RewriteCond directive is a TestString that is evaluated against the second argument (CondPattern). So, instead of using %{REQUEST_FILENAME} you could use %{DOCUMENT_ROOT}%{REQUEST_URI} to construct the absolute file-path for the requested resource. You can construct any string to test against the second argument (eg. to test whether it is a physical file).