Skip to content

Instantly share code, notes, and snippets.

@BMeu
Last active June 22, 2020 12:12
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save BMeu/3a6b61fcc681c1b3e76f07473a9bb39a to your computer and use it in GitHub Desktop.
Save BMeu/3a6b61fcc681c1b3e76f07473a9bb39a to your computer and use it in GitHub Desktop.
Apache: passing the requested URL to a 404 error document

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 exact1), and we can use environment variables in the rewrite module 2! 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:

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:

<Directory /path/to/error/script/>
    Options +ExecCGI
    Require all granted
</Directory>

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:

# Set up newscript.
ScriptAlias /newscript /path/to/new/script/run.cgi/
<Directory /path/to/new/script/>
    Options +ExecCGI
    Require all granted
</Directory>

# 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]

  1. https://httpd.apache.org/docs/2.4/custom-error.html#variables

  2. https://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewritecond

@cmrockwell
Copy link

cmrockwell commented Apr 6, 2017

The following worked for me getting the requested URI from the error pages html

<!--#echo encoding="url" var="REQUEST_URI" -->

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment