Apache: Passing the Requested URL to a 404 Error Document ========================================================= Sometimes you may want to pass the URL that triggered a ``404 Not Found`` error on your Apache server to a script handling the error. Of course, you could simply set the ``ErrorDocument 404 /path/to/error/script`` directive and access the environment variables set by the directive to retrieve the requested URL. But what if this is not an option and you want the requested URL as a parameter when calling the script (see below for an example where this might be useful)? Unfortunately, the ``ErrorDocument`` does not allow rewriting the URL... We could simulate the ``ErrorDocument 404`` behavior using the ``rewrite`` module. Since we do not want to hardcode any URLs into our rewrite conditions, we will have to check if the requested URL exists as a file, a directory, a link, ... on our filesystem using the ``-f``/``-d``/``-l`` flags---but this will also cause valid aliases to be considered non-existing! There is the ``-U`` flag that checks if the requested URL is a valid URL. For some reason (and I really don't know why) it does not work as expected and still throws a normal ``404`` error if the requested URL does not exist, instead of rewriting it to the script. But wait a minute---``ErrorDocument`` guarantees us to set a few environment variables (``REDIRECT_URL``, ``REDIRECT_STATUS``, and ``REDIRECT_QUERY_STRING`` to be exact [#errorenvvar]_), and we can use environment variables in the ``rewrite`` module [#rewriteenvvar]_! So let's combine those two: first, we will have ``ErrorDocument`` pass all ``404`` errors to the location of our script. This will ensure that the above mentioned environment variables will be set and any additional files used by the error page (e.g. CSS and JS) will be accessed at the script's location. Secondly, we will make ``rewrite`` catch all ``404`` errors and append the requested URL to our actual error handling script. So, in your ``.htaccess`` or server configuration file, add the following lines: .. code-block:: apache ErrorDocument 404 /script-location RewriteEngine on RewriteCond %{ENV:REDIRECT_STATUS} "=404" RewriteRule (.*) /path/to/error/script/run.cgi?url=%{ENV:REDIRECT_URL} [L] If your script lies outside of the document root of your server, you might also have to set the correct permissions: .. code-block:: apache Options +ExecCGI Require all granted That's it! Example ------- So where might this be useful? Let's assume you want to add a CGI script to your server, handling its own URLs. Using ``ScripAlias /newscript /path/to/new/script/run.cgi/``, you can give it its own location (``/newscript`` on your server), but you also want it to be called when accessing the root, i.e. ``/``. Since its handling all URLs on its own, setting ``ScriptAlias / /path/to/new/script/run.cgi/`` would cause all URLs to be handled by the new script, and thus making any existing projects effectively unavailable! Therefore, the script should only be executed if the requested URL does not exist---exactly what we did above. We will only need to add a rewrite rule for accessing the root of the server, but that is done with two additional lines. The entire setup would thus look like: .. code-block:: apache # Set up newscript. ScriptAlias /newscript /path/to/new/script/run.cgi/ Options +ExecCGI Require all granted # If the requested URL does not exist, rewrite it to newscript. ErrorDocument 404 /newscript RewriteEngine on RewriteCond %{ENV:REDIRECT_STATUS} "=404" RewriteRule (.*) /path/to/new/script/run.cgi%{ENV:REDIRECT_URL} [L] # Call newscript when accessing the root. RewriteCond %{REQUEST_FILENAME} ^/$ RewriteRule (.*) /path/to/new/script/run.cgi/ [L] .. [#errorenvvar] https://httpd.apache.org/docs/2.4/custom-error.html#variables .. [#rewriteenvvar] https://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewritecond