Simulate Apache mod_rewrite routing

Basic Apache mod_rewrite functionality can be simulated through the use of a PHP script referenced from app.yaml that will in turn load the desired script. This example simulates the common PHP pattern that expects the variable $_GET['q'] to contain the request path.

About mod_rewrite.php

To enable mod_rewrite functionality, your application needs to include mod_rewrite.php, which is the script that will be invoked for all requests to your application to perform the request routing. As described in the comments the script will check for the existence of root-level PHP scripts and invoke them while placing the path portion of $_SERVER['REQUEST_URI'] in the $_GET['q'] variable.

<?php
/**
 * @file
 * Provide basic mod_rewrite like functionality.
 *
 * Pass through requests for root php files and forward all other requests to
 * index.php with $_GET['q'] equal to path. The following are examples that
 * demonstrate how a request using mod_rewrite.php will appear to a PHP script.
 *
 * - /install.php: install.php
 * - /update.php?op=info: update.php?op=info
 * - /foo/bar: index.php?q=/foo/bar
 * - /: index.php?q=/
 */

$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);

// Provide mod_rewrite like functionality. If a php file in the root directory
// is explicitly requested then load the file, otherwise load index.php and
// set get variable 'q' to $_SERVER['REQUEST_URI'].
if (dirname($path) == '/' && pathinfo($path, PATHINFO_EXTENSION) == 'php') {
  $file = pathinfo($path, PATHINFO_BASENAME);
}
else {
  $file = 'index.php';

  // Provide mod_rewrite like functionality by using the path which excludes
  // any other part of the request query (ie. ignores ?foo=bar).
  $_GET['q'] = $path;
}

// Override the script name to simulate the behavior without mod_rewrite.php.
// Ensure that $_SERVER['SCRIPT_NAME'] always begins with a / to be consistent
// with HTTP request and the value that is normally provided.
$_SERVER['SCRIPT_NAME'] = '/' . $file;
require $file;

Example app

The following show a very simple application written to expect $_GET['q'].

app.yaml

As you can see from this app.yaml file, this application will provide two root-level PHP scripts and serve static files out of a directory named downloads.

application: mod_rewrite_simulator
version: 1
runtime: php55
api_version: 1

handlers:
# Example of handler which should be placed above the catch-all handler.
- url: /downloads
  static_dir: downloads

# Catch all unhandled requests and pass to mod_rewrite.php which will simulate
# mod_rewrite by forwarding the requests to index.php?q=... (or other root-level
# PHP file if specified in incoming URL.
- url: /.*
  script: mod_rewrite.php

index.php

index.php is a router-style index.php which reads $_GET['q'] to determine the request path.

<?php

if ($_GET['q'] == '/help') {
  echo 'This is some help text.';
  exit;
}

echo 'Welcome to the site!';

other.php

The following is an example of a root-level script that can be called directly. Many PHP frameworks have scripts like install.php or update.php that would behave similarly.

<?php

echo 'Welcome to the other site.';

Example requests

Given the above example application the following requests would be handled as shown.

  • / translates to index.php with $_GET['q'] = '/'
  • /help translates to index.php with $_GET['q'] = '/help'
  • /other.php translates to other.php with $_GET['q'] = null
  • /downloads/foo_17.png translates to downloads/foo_17.png

Avoid the need for mod_rewrite.php

Many PHP frameworks no longer depend on $_GET['q']. Instead they make use of $_SERVER['REQUEST_URI'] which works both with and without mod_rewrite. As such the latter is the preferred method on App Engine.

As used in mod_rewrite.php a clean method of utilizing REQUEST_URI is the following:

<?php

$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);

if ($path == '/help') {
  echo 'This is some help text.';
  exit;
}

echo 'Welcome to the site!';