PHP, Apache Timeouts, POST Requests

I recently encountered a tough problem to understand. I have a project PHP project based on Laravel. One particular route was a generating a report, storing it to XLS, and downloading it for the user. Its a long report, and may take several minutes to process. (Before you mention we should be using queues, I know). Unexpected behavior was this:

  1. submit a POST to a route.
  2. system pulls data from database and from external APIs like Stripe and Easypost.
  3. system generates Excel spreadsheet for download.
  4. approximately 100 seconds into the process, the browser would redirect to the same route, but using a GET request.

If I chose shorter date ranges, it ran wonderfully. I knew it was a timeout behavior. Here are the steps I took to find the problem.

  1. PHP set_time_limit: PHP has a time limit set in its ini file. If you’re running 5.4 or newer, there is no safe_mode so you can use this to modify the time_limit. I set it to 0 as this report was behind an admin panel. If they want a long report, its up to them.
  2. File Generation: I used PHPExcel to generate the binary data. Originally, I was pushing it out php://output and setting headers directly. I changed it to store the file locally in a tmp directory (possible security issue, but there wasn’t anything sensitive in the file). Then I used Laravel’s Response::download() facade to push it down to the user.
  3. Apache’s TimeOut – Apache has a builtin timeout behavior as well. When the Apache process times out, it sends a 500 error code. Apparently, browsers when a 500 error code response is sent to a POST, the browser will respond with a GET request to the same URL. That’s why my page looked as if I was getting a redirect. The server wasn’t redirecting it, the browser was. Both Firefox and Chrome seemed to have the same bahavior. After bumping the Apache TimeOut length, I no longer suffered any issues.

This is the first time I’ve experienced getting a 500 code for a POST request and having the browser turn around and do a GET request for it. I hope this helps some poor soul suffering a similar issue.

Leave a Reply

Your email address will not be published. Required fields are marked *