0
votes

Short version:

I got a request (no trailing slash)

mydomain.com/slides/sessionOne

How can I figure out in a RewriteCond if '_slides/sessionOne' exists and is a directory? If so, append a trailing / (but do not prepend a _) and redirect [R=301].

Long version:

I have a fairly empty webroot:

.htaccess
_slides  (dir)
_site    (dir)

and a .htaccess that almost does what I want:

RewriteEngine On
RewriteBase /

# directly accessing an _underscore-Folder? – Forbid!
RewriteRule ^_ - [R=403,END]

# AAA    

RewriteRule ^slides/(.+)$  _slides/$1 [END]

# prevent _site prepend before _slides
RewriteCond %{REQUEST_URI} !^/_(site|slides)
RewriteRule ^(.*)$  _site/$1 [END]
  • any URL under slides/ will find it's stuff in the _slides folder
  • any other url will go and look inside the _site folder
  • direct access to both underscore-folders is prevented

Directory-URLs with trailing slash work fine, just like files: For example slides/presentation1/ will internally find _slides/presentation1/index.html w/o any visible URL change, as it should.

My trouble: Directory-URLs without trailing slash:

  • slides/presentation1 will first get (internal, non-visible) rewritten to _slides/presentation1
  • since the slash is missing, but there is a directoy of that name, Apache automatically makes a visible 301 redirect, sending me to _slides/presentation1/.

But I do not want to reveal the underscore-Folders, and this (correctly) gets caught by my check.

So, I would need at the “AAA” position is in pseudo-code:

URI has no trailing slash
AND there exists a directory either under _slides oder _site?
(peeking for it)

==> make a 301 redirect!
but no underscores prepended, just a trailing slash attached...

Here's my attempt (at the AAA position), but it's just not working:

# directory w/o trailing slash inside _slides/ ?
# ==> attach slash and 301
RewriteCond %{REQUEST_URI} [^/]$
RewriteCond _slides/%{REQUEST_FILENAME} -d [OR]
RewriteCond _site/%{REQUEST_FILENAME} -d
RewriteRule ^ %{REQUEST_URI}/ [R=301,L]
1

1 Answers

0
votes

Toughie, but I found a way:

  1. generate a (server-absolute) path to peek for a valid directory (the -d does not care, if your path has 0,1 or even multiple trailing slashes). For generation I use rewrite rules, that don't rewrite anything (“-”), but store what I need in an env var. Only do that for URLs in question, that is: w/o trailing (→[^/]).
  2. Otherwise the env var remains empty. (one of the two, certainly will. Can't match both.)
  3. for applicable (aka non-empty) Env-Vars, check if they are is a directory, if so, append trailing slash, do a 301. (to make the slash-append visible. Like any webservers does..., just under tougher circumstances...).
  4. if there was no 301 (resp. that run happened before), redirect as described in the question.

Code:

RewriteEngine On
RewriteBase /

# already on _underscore-Folder? – Forbid!
RewriteRule ^_ - [R=404,L,END]

# at most one of the two will become valid
RewriteRule ^(slides/.+[^/])$ - [E=slidesPeek:%{DOCUMENT_ROOT}_$1]
RewriteRule ^(?!slides/)(.*[^/])$ - [E=sitePeek:%{DOCUMENT_ROOT}_site/$1]

RewriteCond %{ENV:slidesPeek} !^$
RewriteCond %{ENV:slidesPeek} -d
RewriteRule ^.*$ $0/ [R=301,L,END]

RewriteCond %{ENV:sitePeek} !^$
RewriteCond %{ENV:sitePeek} -d
RewriteRule ^.*$ $0/ [R=301,L,END]

RewriteRule ^slides/(.+)$  _slides/$1 [L,END]
RewriteCond %{REQUEST_URI} !^/_(site|slides)
RewriteRule ^(.*)$  _site/$1 [END]

Bonus 8-) Useful for testing and debugging:

RewriteRule ^(?!DEBUG)\w+$ /DEBUG?%{DOCUMENT_ROOT} [R=301,END]

Caveat:

  • Flag [L] means: stop for the rest of this .htacess-file (but possible do additional rounds from start. On 301 redirects and on internal rewrites)
  • Flag [END] means: No additional rounds... (except for 301, which inevitably starts the whole thing). [END] does NOT imply [L]. Without [L], rules that follow would still be applied, just no additional rounds

Something, that one won't notice on more trivial cases.

Just for reference: (mind all the slashes)

%{DOCUMENT_ROOT}
/is/htdocs/user12345678/www.mydomain.de/

%{REQUEST_FILENAME}
/is/htdocs/user12345678/www.mydomain.de/abc

%{REQUEST_URI}
uri=/abc

Preceeding slashes are a constant pain by the way: RewriteRule get's the URL without initial slash, whereas %{REQUEST_URI} has one...

I honestly no longer understand, why the seconds-last line is needed. But deletion gets me 404s...


Advice:

Browsers notoriously cache your prior 301-redirects (keep sending you there again, without checking.)

Thus either you need to keep cleaning caches all the time or you do your testing from the command line, i.e.

cd someTemporaryFolder
wget http://mywhateverurl.com 2>&1

resp.

wget http://mywhateverurl.com 2>&1 | grep Location: