For over 10 years I uploaded all sorts of files to the root of my webserver.
/
oldphoto.jpg
oldjunk.txt
oldfolder/
oldfile.txt
newpage.html
newimg.png
There's now ~1800 files in the root. My FTP client is slow to retrieve directory listing, so managing the website is difficult.
I'd like to tidy this up. I know which files I want to be stored at root-level (new stuff). Everything else, I've moved into a legacy/ folder.
/
legacy/
oldphoto.jpg
oldjunk.txt
oldfolder/
oldfile.txt
newpage.html
newimg.png
Problematically: some of those old files are still accessed today, from various external websites.
I want to make a cunning mod_rewrite rule that works like this:
file didn't exist?
okay, does it exist in the legacy/ folder?
then, I'll redirect you to the appropriate file in the legacy/ folder
So the following cases need to work:
#[A] file does not exist in /, exists in legacy folder: redirect
http://example.com/oldphoto.jpg -> http://example.com/legacy/oldphoto.jpg
#[B] file does not exist in /, exists in legacy folder: redirect
http://example.com/oldfolder/oldfile.txt -> http://example.com/legacy/oldfolder/oldfile.txt
#[C] file does not exist in /, does not exist in legacy folder: 404 as usual
http://example.com/not-exist.txt -> 404
#[D] file exists in /: serve page as usual
http://example.com/newpage.html -> http://example.com/newpage.html
I got pretty close to getting this working:
RewriteEngine on
# No such file exists:
RewriteCond %{SCRIPT_FILENAME} !-f
# No such directory exists:
RewriteCond %{SCRIPT_FILENAME} !-d
# Capture the head of REQUEST_URI into %2 backreference; this tells us the absolute path to our web root
RewriteCond %{REQUEST_URI}::%{SCRIPT_FILENAME} ^(.*?)::(.*)\1$
# File exists web_root/legacy/REQUEST_URI OR
RewriteCond %2/legacy/%{REQUEST_URI} -f [OR]
# Directory exists web_root/legacy/REQUEST_URI OR
RewriteCond %2/legacy/%{REQUEST_URI} -d
# Redirect to /legacy/REQUEST_URI
RewriteRule .* /legacy/%{REQUEST_URI} [L,R=301]
But this only solves cases A,C, and D. The nested case (B) fails, because %{SCRIPT_FILENAME} is not what I thought it was.
I am testing the redirect like so:
curl -sI 'http://example.com/oldphoto.jpg' | grep Location | sed 's/^Location: //'
http://example.com/legacy//oldphoto.jpg
Here is what the macros expand to:
# when requesting 'http://example.com/oldphoto.jpg':
SCRIPT_FILENAME: /customer/homepages/13/c12345678/htdocs/user/oldphoto.jpg
REQUEST_FILENAME: /customer/homepages/13/c12345678/htdocs/user/oldphoto.jpg
DOCUMENT_ROOT: /var/www/html
REQUEST_URI: /oldphoto.jpg
THE_REQUEST: HEAD /oldphoto.jpg HTTP/1.1
# when requesting 'http://example.com/oldfolder/oldfile.txt':
SCRIPT_FILENAME: /customer/homepages/13/c12345678/htdocs/user/oldfolder
REQUEST_FILENAME: /customer/homepages/13/c12345678/htdocs/user/oldfolder
DOCUMENT_ROOT: /var/www/html
REQUEST_URI: /oldfolder/oldfile.txt
THE_REQUEST: HEAD /oldfolder/oldfile.txt HTTP/1.1
We can see that %{SCRIPT_FILENAME} does not give the full path to my file.
We can also see that %{DOCUMENT_ROOT} cannot be relied upon to give us the absolute path to my web root.
If somebody requests http://example.com/oldfolder/oldfile.txt:
- how do I check for the existence of
/customer/homepages/13/c12345678/htdocs/user/legacy/oldfolder/oldfile.txt? - how do I redirect the user to
http://example.com/legacy/oldfolder/oldfile.txt?
I assume that /customer/homepages/13/c12345678/htdocs/user/ is likely to change (I have managed web hosting), so I would prefer not to hard-code it.
I am surprised that %{DOCUMENT_ROOT} does not give me this. Maybe it gives logical web root instead of physical web root.
I am also surprised that %{SCRIPT_FILENAME} gives me …/oldfolder rather than the path suggested in %{REQUEST_URI}: …/oldfolder/oldfile.txt.