Odd Apache pitfall: ErrorDocument and POST

I am writing a web app that hides much of its inner workings, as any good web app should. (Why? future-proofing, security, simplicity of user experience, etc.) I chose to route all requests that involve server-side scripting through a single file, capture.php. But somewhere along the way, form POSTs stopped working! Why?

The setup

I prefer to keep the number of root-level files to a minimum, separating everything else into directories on the basis of file type.

  • .htaccess - contents shown below
  • capture.php - all scripting requests are routed through here
  • scripts/ - server-side scripting, pulled in by capture.php
    • .htaccess - Deny from all
    • main.php, about.php, etc.
  • images/, css/, js/ - files that can be requested directly

The root .htaccess file uses mod_rewrite to assign capture.php to all the "fake" URLs my web app uses, such as "/main" and "/about/author".

RewriteBase /

# Handles /
DirectoryIndex capture.php

# Handles /scripts/main.php and such
ErrorDocument 403 capture.php
ErrorDocument 404 capture.php

# All those web app paths that don't match the filesystem, like /main
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /capture.php [L]

# And, of course, capture.php handles itself

I also placed a header("HTTP/1.1 200 OK"); statement into the main processing script, since any request that triggered an ErrorDocument directive would have a 403 or 404 status code.

The symptoms

I was having a heck of a time debugging the login page, and narrowed it down to this: Upon POST, both $_POST and $HTTP_POST_VARS remained empty.

Oddly enough, when I posted the form to the main page ("/"), the POST arrays were populated. Curiouser and curiouser.

The forehead slap

All it took to make the whole thing work was one little line inserted at the top of the .htaccess file:

RewriteEngine On

I had taken it out, thinking that it was enabled elsewhere. The ErrorDocument 404 directive disguised this by mimicking some of the behavior of the RewriteCond directives — no doubt I inserted it after receiving a 404 response. (Instead, that should have raised a red flag about the rewrite engine.)

In any case, it seems that $_POST is not populated when an ErrorDocument is used. Here's my new .htaccess file:

RewriteEngine On
RewriteBase /

DirectoryIndex capture.php

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /capture.php [L]

I've also taken out the header("HTTP/1.1 200 OK") call, because capture.php is returning 200 OK, as it should. As for 403 errors, I think I will have to add a rules to the scripts/.htaccess file that returns a 404 error instead of a 403.


Responses: 7 so far

  1. Xaprb says:

    Before I knew about mod_rewrite, I used an 404 trick. What a hack. Every request for a .html file would fail because there was no such file. Then Apache would run the 404 page, which was a PHP file that looked at the environment to see see what file had originally been requested. I discovered the same thing: no POST data. I think it's because the 404 document is actually invoked from a new request Apache creates, after discarding the original one.

    My next hack was using an AddHandler trick:

    AddHandler headered .html
    Action headered /includes/site.php

    Worked okay, but I still just needed to learn about mod_rewrite!

  2. Tim McCormack says:

    We've all done that kind of thing. Mostly before we realize, "Hey, I can't be the only person that's wanted this feature before, maybe it's already been implemented the *right* way."

    And then you discover there's a name for what you wanted to do, and several competing software packages, and a whole website on the relevant best practices.

  3. Juan Sarria says:

    Wow. Thanks a lot. You saved a lot of time. Great job!

  4. Bijan Vakili says:

    Thanks! This saved me a lot of headache!

  5. Shashank Duhan says:

    Thanks!! It worked..... :)

  6. David says:

    THANK YOU VERY MUCH

  7. W-prog says:

    Great !!! Thanks a lot