Php – Make mod_rewrite rules process before fcgi sees the request

apache-2.2fastcgimod-rewritePHP

Is there a way to have the mod_rewrite rules in my .htaccess file take effect before PHP behind fcgi sees the request?

I have an email validation link that I want to redirect from root to the login page before PHP marks it in the database as confirmed.

These are the rules I have in my .htaccess, and they work, but PHP sees the request before the rewrite rule takes effect.

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{QUERY_STRING} activation_key=
    RewriteRule ^$ /login [R,L]
    RewriteBase /
    RewriteRule ^index\.php$ - [L]
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . /index.php [L]
</IfModule>

When PHP sees a request that contains the activation_key query string, it marks the user as activated. If the user is already activated, the page will show an "Unknown validation link" error.

With these rewrite rules the request redirects to the /login page and displays the error, indicating – I assume – that PHP had already seen the request.

This is the mod_fastcgi config:

<IfModule mod_fastcgi.c>
    DirectoryIndex index.html index.shtml index.cgi index.php
    AddHandler php5-fcgi .php
    Action php5-fcgi /php5-fcgi
    Alias /php5-fcgi /usr/lib/cgi-bin/php5-fcgi
    FastCgiExternalServer /usr/lib/cgi-bin/php5-fcgi -host 127.0.0.1:9000 -pass-header Authorization
</IfModule>

Best Answer

If I'm understanding this correctly:

  1. You make a request to /?activation_key=foobar and Apache sends back a response to redirect you to /login.
  2. You make a followup request to /login and that page says it has already seen the validation link. But there is no activation_key in this request, it only existed in the first request. It has probably seen the empty string before and that's why it's throwing an error now. (The exact logic in your PHP script may not match this explanation but I suspect it's something similar.)

Try changing your rewrite rules like this:

RewriteCond %{QUERY_STRING} activation_key=[^&]*
RewriteRule ^$ /login?activation_key=%1 [R,L]

That captures the activation key and passes it along on the next request to /login so that PHP will then have the key.

It might be worth while you're debugging to echo out or log to a file the query string or the whole request or even the whole $_SERVER super global.


Your last rewrite rule has an extra / in it that doesn't need to be there. This would be a better rule:

RewriteRule ^ index.php [L]

.htaccess files and the associated AllowOverride all directive carry a performance penalty with them. This becomes painfully obvious when using strace and watching all the file operations looking for all the places a .htaccess file could be hiding. If you're running your own Apache server rather than being on shared hosting, you might as well put these rules in the main config and set AllowOverride to none. Inside a <Directory> block they will work as they are. Anywhere else they will need an extra leading slash.