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 [1]), 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!
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 |
The following worked for me getting the requested URI from the error pages html
<!--#echo encoding="url" var="REQUEST_URI" -->