Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I am writing a CMS type application and I am trying to restrict how the server gets accessed.

  • Any content (in js/, css/ or images/ directories) should be served as is, as long as there is an index.php file at the same level as the content directory
  • All other paths should run through index.php to determine what content gets displayed
  • The executed index.php file must not appear in the URL, although other instances must not get removed, in case of embedding another URL within the parameter part of the URL
  • There must be only one .htaccess file
  • The .htaccess file must work without modification not matter which directory it is placed in

I don't have access to the httpd.conf file on the hosted server. The .htaccess file is below. It sets REWRITE_BASE to the directory the .htaccess file is in, making it the "virtual" web root. It also sets INDEX_PATH and INDEX_PARAMS which are the bits before and after where the index.php would be in the URL if it hadn't been taken out.

For example: http://somedomain.net/testsite/foo/bar/baz/qux/

The .htaccess file is in testsite/. There is an index.php file in testsite/ and another one in testsite/foo/bar/. When the above URL is requested the script in testsite/foo/bar/ will get executed. PHP will have access to three additional fields in $_SERVER: REWRITE_BASE => '/testsite/', INDEX_PATH => 'foo/bar/' & INDEX_PARAMS => 'baz/qux/'.

It seems to behave properly. Can the code/rules be simplified? Am I using any unnecessary flags? Have I missed any flags that would really help? Is there a better way of serving the final matched file other than setting E=FINAL:true and testing for it in the next iteration?

Options -Indexes
DirectoryIndex

RewriteEngine On

# Find directory that this .htaccess file is saved in
# relative to DOCUMENT_ROOT and store the value in ENV:REWRITE_BASE
# The value will always begin and end with a slash: e.g. /, /dir1/, /dir1/dir2/, etc.
# Leave RewriteBase set to /. This file can now be moved without needing to be edited
# Use %{ENV:REWRITE_BASE} at the start of every path replacement pattern
RewriteBase /
RewriteCond $0#%{REQUEST_URI} ([^#]*)#(.*)\1$
RewriteRule ^.*$ - [E=REWRITE_BASE:%2]

# Remove REDIRECT_.* copies of variables
RewriteRule ^ - [E=INDEX_PATH:%{ENV:REDIRECT_INDEX_PATH},E=!REDIRECT_INDEX_PATH,E=INDEX_PARAMS:%{ENV:REDIRECT_INDEX_PARAMS},E=!REDIRECT_INDEX_PARAMS,E=!REDIRECT_REWRITE_BASE,E=!REDIRECT_STATUS]

# If FINAL was set in the last iteration (now REDIRECT_FINAL)
# don't redirect again, stop processing ([L]ast rule) and unset REDIRECT_FINAL
RewriteCond %{ENV:REDIRECT_FINAL} true
RewriteRule ^ - [L,E=!REDIRECT_FINAL]

# If index.php and one of js/, css/, images/ exist at the same level serve the static file
RewriteCond %{DOCUMENT_ROOT}%{ENV:REWRITE_BASE}$0 -d
RewriteCond %{DOCUMENT_ROOT}%{ENV:REWRITE_BASE}$1index.php -f
RewriteRule ^(.*/)?(js|css|images)/$ %{ENV:REWRITE_BASE}$0%{ENV:INDEX_PARAMS} [L,E=FINAL:true]

# If there is an index.php file in the current directory, and the end of the URI is /index.php
# redirect (302 test, 301 production) to without /index.php, adding the INDEX_PARAMS back on
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^(.*/)?index.php/?$ %{ENV:REWRITE_BASE}$1%{ENV:INDEX_PARAMS} [L,R=302]

# If there is an index.php file in the current directory, serve it
RewriteCond %{REQUEST_FILENAME}/index.php -f
RewriteRule ^.*$ %{ENV:REWRITE_BASE}$0index.php [L,NS,E=FINAL:true]

# If the current directory doesn't contain index.php
# record the path to the current directory and the path removed afterwards
# Restart the rules, trying one directory further down
RewriteCond %{REQUEST_FILENAME}/index.php !-f
RewriteRule (.*/)?(.+)$ %{ENV:REWRITE_BASE}$1 [L,QSA,E=INDEX_PATH:$1,E=INDEX_PARAMS:$2%{ENV:INDEX_PARAMS}]

# If there is no index.php file at the same level as this .htaccess file
# reset the URI to the original request and allow Apache to process it (e.g. 404)
RewriteRule ^$ %{ENV:REWRITE_BASE}%{ENV:INDEX_PARAMS} [L,E=!INDEX_PATH,E=!INDEX_PARAMS,E=!REWRITE_BASE,E=FINAL:true]
share|improve this question

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Browse other questions tagged or ask your own question.