Forum Moderators: phranque

Message Too Old, No Replies

Use a subdirectory as document root with htaccess in Apache 1.3

RewriteRules work in 2.2, not in 1.3 because of trailing slashes…

         

andrewheiss

7:28 pm on Mar 11, 2010 (gmt 0)

10+ Year Member



I have blog-ish site that is created with a static site generator—I generate the site on my local machine and rsync it up to the server. I'd like to keep the entire site in a separate folder inside the root, for organization's sake, like so:

/
/site/ # this directory gets rsynced
/site/about/ # actual page is index.html; DirectoryIndex correctly hides it
/site/sample-page/
/site/blog/2010/01/01/some-post/
/other/
/folders/
/on/
/the/
/server/


I'd like to use the contents of /site/ as the document root unless a file similarly named exists in the actual web root. So something like /site/sample-page/ would show as example.com/sample-page/, while something like /other-folder/ would display as example.com/other-folder

I have this working flawlessly in Apache 2.2:

RewriteEngine On

# Map http://www.example.com to /site.
RewriteRule ^$ /site/ [L]

# Map http://www.example.com/x to /site/x unless there is a x in the web root.
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !^/site/
RewriteRule ^(.*)$ /site/$1

# Add trailing slash to directories without them so DirectoryIndex works.
# This does not expose the internal URL.
RewriteCond %{REQUEST_FILENAME} -d
RewriteCond %{REQUEST_FILENAME} !/$
RewriteRule ^(.*)$ $1/

# Disable auto-adding slashes to directories without them, since this happens
# after mod_rewrite and exposes the rewritten internal URL, e.g. turning
# http://www.example.com/about into http://www.example.com/site/about
DirectorySlash off


However, my production server runs Apache 1.3, which doesn't allow DirectorySlash. If I disable it, the server gives a 500 error because of internal redirect overload.

If I comment out the last section of ReWriteConds and rules:

RewriteCond %{REQUEST_FILENAME} -d
RewriteCond %{REQUEST_FILENAME} !/$
RewriteRule ^(.*)$ $1/


…everything mostly works: example.com/sample-page/ displays the correct content. However, if I omit the trailing slash, the URL in the address bar exposes the real internal URL structure: example.com/site/sample-page/

In essence, I have a combination of two of the issues listed in the 1.3 URL Rewriting Guide, Moved DocumentRoot and the Trailing Slash Problem.

What is the best way to account for directory slashes in Apache 1.3, where useful tools like DirectorySlash don't exist? How can I use the /jekyll/ directory as the site root without revealing the actual URL structure? In other words, how can I translate the working Apache 2.2 rewrite rules to work in 1.3?

jdMorgan

8:19 pm on Mar 14, 2010 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



This should work on either Apache 1.3.x or 2.x:

RewriteEngine On
#
# Externally redirect to add missing trailing slashes to directory requests
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^(.*[^/])$ http://www.example.com/$1/ [R=301,L]
#
# Map http://www.example.com/ to /site/ subdirectory
RewriteRule ^$ /site/ [L]
#
# Map http://www.example.com/x to /site/x unless there is a x in the web root.
RewriteCond $1 !^site/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /site/$1 [L]

Where possible, all other rewriteconds are moved above the file- and directory-exists check rewriteconds and rewriterule patterns are mad more-specific to avoid unnecessary calls to the OS to go check the disk. This operation is very inefficient, and should be avoided whenever possible. Consider adding additional exclusions for filetypes which need not be re-mapped, so that more of these exists-checks can be avoided.

Jim

andrewheiss

8:56 pm on Mar 14, 2010 (gmt 0)

10+ Year Member



Unfortunately that's still not working all the way. http://www.example.com/about gets redirected to http://www.example.com/site/about/ instead of http://www.example.com/about/

andrewheiss

9:50 pm on Mar 14, 2010 (gmt 0)

10+ Year Member



There we go. Finally got it.

RewriteEngine On

# Map http://www.example.com to /site.
RewriteRule ^$ /site/ [L]

# Map http://www.example.com/x to /site/x unless there is a x in the web root.
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !^/site/
RewriteRule ^(.*)$ /site/$1

# Add trailing slash to directories within /site
# This does not expose the internal URL.
RewriteCond %{SCRIPT_FILENAME} -d
RewriteRule ^site/(.*[^/])$ http://www.example.com/$1/ [R=301]


It's probably not über efficient, but it works.

g1smd

11:08 pm on Mar 14, 2010 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



Redirects must always be listed before rewrites, because redirects have an effect on the URL the user sees in the browser's address bar.

Add the [L] flag to every rule unless you know of a very good reason (rare!) that it can be omitted.

Make your -d and -f checks be the last conditions within that ruleset, not the first.

I'm not sure that (.*[^/]) is the best form, I usually use ([^/]+)$ or similar.

jdMorgan

1:03 am on Mar 15, 2010 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



With " (.*[^/])", we are requiring that at least one non-slash character end the requested URL-path. The URL path may contain other slashes and characters, as long as it doesn't end with a slash. Since we are only concerned with the final character, the greediness of ".*" is not a problem as only a single back-off-and-retry is required for a match.

Again, put as many qualifications as possible before the -d and -f checks, as described above. Otherwise, you are beating on your disk and slowing down user requests unnecessarily. Such waste leads to prematurely-required server upgrades.

Jim