Take the 2-minute tour ×
Server Fault is a question and answer site for professional system and network administrators. It's 100% free, no registration required.

I have a small PHP/Apache2 web application wherein I'd like to do two seemingly incompatible operations:

  1. Route all requests through a single PHP script (a "front controller", if you will)
  2. Secure everything except API calls with HTTP basic authentication

I can satisfy either requirement just fine in isolation, it's when I try to do both at once that I am blocked. For no good reason I'm trying to accomplish these requirements solely with Apache configuration.

Here are the requirements stated as an example. A GET request for this URL:

http://basic/api/listcars?max=10

should be sent through front.php without requiring basic auth. front.php will get /api/listcars?max=10 and do whatever it needs to with that.

Here's what I think should work. In my /etc/hosts I added

127.0.0.1 basic

and I am using this Apache config:

<Location />
  AuthType Basic
  AuthName "Home Secure"
  AuthUserFile /etc/apache2/passwords
  require valid-user
</Location>

<VirtualHost *:80>
  ServerName basic
  DocumentRoot /var/www/basic
  <Directory /var/www/basic>
    <IfModule mod_rewrite.c>
      RewriteEngine On
      RewriteCond %{SCRIPT_FILENAME} !-f
      RewriteCond %{SCRIPT_FILENAME} !-d
      RewriteRule ^(.*)$ /front.php/$1 [QSA,L]
    </IfModule>
  </Directory>
  <Location /api>
    Order deny,allow
    Allow from all
    Satisfy any
  </Location>
</VirtualHost>

# another extant vhost in the same Apache2 config/server, for completeness
<VirtualHost *:80>
  ServerName dustbin
  ServerAlias *.dustbin
  DocumentRoot /var/www/dustbin
</VirtualHost>

But I still always get a HTTP 401: Authorization Required response. I can make it work by changing <Location /api> into

<Location ~ /api>

but this allows more than I want to past basic auth.

I also tried changing the <Directory /var/www/basic> section into <Location />, but this doesn't work either (and it results in some strange values for PATH_TRANSLATED being passed to the script).

I searched around and found many examples of selective exclusion of basic auth, but none that also incorporated a front controller.

I could certainly do something like handle basic auth in the front controller, but if I can have Apache do that instead I'll be able to keep all authentication logic out of my PHP code.

A friend suggested splitting this into two vhosts, which I know also works. This used to be two separate vhosts, actually.

<Location /> is outside the vhost because I want all vhosts to require basic auth by default. The one exception I wish to make is for all URIs starting with /api on this particular vhost. I omitted other vhosts because they didn't seem relevant to my question.

I'm using Apache 2.2.22 / PHP 5.3.10 on Ubuntu 12.04.

share|improve this question

1 Answer 1

up vote 1 down vote accepted

Because the RewriteRule that's modifying the path is in a <Directory> block (which was done in order to make %{SCRIPT_FILENAME} have the full file path), the [PT] flag is implied on the rule. Because the [PT] flag applies, the URL of the new request is re-mapped against locations - and the <Location /api> section no longer applies to the request.

So, given that, there's a few options.

  • Take your rewrite rules out of the <Directory> block.

    <VirtualHost *:80>
      ServerName basic
      DocumentRoot /var/www/basic
      RewriteEngine On
      RewriteCond /var/www/basic%{REQUEST_URI} !-f
      RewriteCond /var/www/basic%{REQUEST_URI} !-d
      RewriteRule ^(.*)$ /front.php/$1 [QSA,L]
      <Location /api>
        Order deny,allow
        Allow from all
        Satisfy any
      </Location>
    </VirtualHost>
    
  • Use that regex <Location> - it can be restricted to prevent unintended matches.

    <Location ~ "(^|^/front\.php)/api/[^/]*$">
    
  • Use FallbackResource instead of mod_rewrite.

    FallbackResource /front.php
    
share|improve this answer
    
Doesn't work--all resources on this vhost require basic auth. <Location /> is outside the vhost because I want all vhosts to require basic auth by default. The one exception I wish to make is for all URIs starting with /api on this particular vhost. Sorry, I omitted other vhosts because they didn't seem relevant to my question. –  Adam Monsen Jun 11 '12 at 4:48
    
@AdamMonsen Well, yeah, that's definitely relevant. Can you provide an example of what those other vhosts look like, as well as any other global config blocks that might have an impact on authorization (anything with Order, Allow, or Deny)? –  Shane Madden Jun 11 '12 at 6:06
    
There is no other global config (anything outside a vhost) mentioning Order, Allow, or Deny. Why are other vhost sections relevant? Are their access controls not isolated from one another? –  Adam Monsen Jun 11 '12 at 14:53
    
@AdamMonsen I want to make sure I'm not going to recommend something for your global config block that's going to blow up your other vhosts. –  Shane Madden Jun 11 '12 at 15:14
2  
Thanks for the help! By the way, I just noticed Apache 2.2 adds the FallbackResource directive which makes the use of mod_rewrite unnecessary for this case. FallbackResource front.php does the right thing! –  Adam Monsen Jun 13 '12 at 6:23

Your Answer

 
discard

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.