Any suggestion is more than welcome! I had to do a few tricks in rewrite rules (checks on ENV:REDIRECT_STATUS
) to avoid infinite loops, but it works fine.
error.php
<?php
$code = 0;
if (isset($_GET['code']))
$code = (int)$_GET['code'];
else if (isset($_SERVER['REDIRECT_STATUS']))
$code = (int)$_SERVER['REDIRECT_STATUS'];
switch ($code)
{
case 400:
$message = 'Bad Request';
break;
case 401:
$message = 'Unauthorized';
break;
case 402:
$message = 'Payment Required';
break;
case 403:
$message = 'Forbidden';
break;
case 404:
$message = 'Not Found';
break;
case 405:
$message = 'Method Not Allowed';
break;
case 406:
$message = 'Not Acceptable';
break;
case 407:
$message = 'Proxy Authentication Required';
break;
case 408:
$message = 'Request Timeout';
break;
case 409:
$message = 'Conflict';
break;
case 410:
$message = 'Gone';
break;
case 411:
$message = 'Length Required';
break;
case 412:
$message = 'Precondition Failed';
break;
case 413:
$message = 'Request Entity Too Large';
break;
case 414:
$message = 'Request-URI Too Long';
break;
case 415:
$message = 'Unsupported Media Type';
break;
case 416:
$message = 'Requested Range Not Satisfiable';
break;
case 417:
$message = 'Expectation Failed';
break;
case 418:
$message = 'I\'m a Teapot';
break;
case 421:
$message = 'Misdirected Request';
break;
case 422:
$message = 'Unprocessable Entity';
break;
case 423:
$message = 'Locked';
break;
case 424:
$message = 'Failed Dependency';
break;
case 425:
$message = 'Unordered Collection';
break;
case 426:
$message = 'Upgrade Required';
break;
case 428:
$message = 'Precondition Required';
break;
case 429:
$message = 'Too Many Requests';
break;
case 431:
$message = 'Request Header Fields Too Large';
break;
case 451:
$message = 'Unavailable For Legal Reasons';
break;
case 500:
$message = 'Internal Server Error';
break;
case 501:
$message = 'Not Implemented';
break;
case 502:
$message = 'Bad Gateway';
break;
case 503:
$message = 'Service Unavailable';
break;
case 504:
$message = 'Gateway Timeout';
break;
case 505:
$message = 'HTTP Version Not Supported';
break;
case 506:
$message = 'Variant Also Negotiates';
break;
case 507:
$message = 'Insufficient Storage';
break;
case 508:
$message = 'Loop Detected';
break;
case 509:
$message = 'Bandwidth Limit Exceeded';
break;
case 510:
$message = 'Not Extended';
break;
case 511:
$message = 'Network Authentication Required';
break;
default:
$code = '???';
$message = 'Unknown Error';
break;
}
echo $code.' '.$message;
?>
.htaccess
############
# SETTINGS #
############
Options All +FollowSymlinks -ExecCGI -Indexes -MultiViews
DirectoryIndex index.php
DirectorySlash On
IndexIgnore *
SetEnv TZ [Your Time Zone]
############
# BROWSERS #
############
BrowserMatch MSIE\ 6\. image-toolbar
BrowserMatch MSIE\ (?:8|9|10)\. legacy-document
Header set imagetoolbar "no" env=image-toolbar
Header set X-UA-Compatible "IE=edge,chrome=1" env=legacy-document
<FilesMatch "\.(?:atom|bin|bmp|css|cur|eot|exe|gif|g?zip|gz|ico|jp(?:eg?|g)|js(?:on)?|og[agv]|opus|otf|pdf|png|rdf|rss|svgz?|swf|tiff?|tt[cf]|txt|webp|woff2?|xml)$">
Header unset X-UA-Compatible env=legacy-document
</FilesMatch>
###################
# FILES - CACHING #
###################
FileETag None
Header unset ETag
Header unset Pragma
ExpiresActive on
ExpiresDefault A2592000
# Data Interchange Files
ExpiresByType application/atom+xml A3600
ExpiresByType application/json A0
ExpiresByType application/rdf+xml A3600
ExpiresByType application/rss+xml A3600
ExpiresByType application/xml A0
# Image Files
ExpiresByType image/bmp A31536000
ExpiresByType image/gif A31536000
ExpiresByType image/jpeg A31536000
ExpiresByType image/png A31536000
ExpiresByType image/svg+xml A31536000
ExpiresByType image/tiff A31536000
ExpiresByType image/webp A31536000
ExpiresByType image/x-icon A31536000
# Script Files
ExpiresByType application/javascript A31536000
# Style Files
ExpiresByType text/css A31536000
# Other Files
ExpiresByType text/html A0
#######################
# FILES - COMPRESSION #
#######################
SetEnvIfNoCase ^(?:Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^(?:(?:gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ has-accept-encoding
RequestHeader append Accept-Encoding "gzip,deflate" env=has-accept-encoding
AddOutputFilterByType DEFLATE application/atom+xml
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/json
AddOutputFilterByType DEFLATE application/rdf+xml
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/vnd.ms-fontobject
AddOutputFilterByType DEFLATE application/x-font-ttf
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE font/opentype
AddOutputFilterByType DEFLATE image/bmp
AddOutputFilterByType DEFLATE image/svg+xml
AddOutputFilterByType DEFLATE image/tiff
AddOutputFilterByType DEFLATE image/x-icon
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/plain
AddEncoding gzip .svgz
####################
# FILES - ENCODING #
####################
AddLanguage en .atom
AddLanguage en .css
AddLanguage en .js
AddLanguage en .json
AddLanguage en .rdf
AddLanguage en .rss
AddLanguage en .txt
AddLanguage en .xml
AddDefaultCharset UTF-8
AddCharset UTF-8 .atom
AddCharset UTF-8 .css
AddCharset UTF-8 .js
AddCharset UTF-8 .json
AddCharset UTF-8 .rdf
AddCharset UTF-8 .rss
AddCharset UTF-8 .xml
####################
# FILES - HANDLERS #
####################
AddHandler application/x-httpd-php .css
AddHandler application/x-httpd-php .js
######################
# FILES - MIME TYPES #
######################
# Archive Files
AddType application/x-gzip .gz .gzip
AddType application/zip .zip
# Data Interchange Files
AddType application/atom+xml .atom
AddType application/json .json
AddType application/rdf+xml .rdf
AddType application/rss+xml .rss
AddType application/xml .xml
# Font Files
AddType application/font-woff .woff
AddType application/font-woff2 .woff2
AddType application/vnd.ms-fontobject .eot
AddType application/x-font-ttf .ttc .ttf
AddType font/opentype .otf
# Image Files
AddType image/bmp .bmp
AddType image/gif .gif
AddType image/jpeg .jpe .jpeg .jpg
AddType image/png .png
AddType image/svg+xml .svg .svgz
AddType image/tiff .tif .tiff
AddType image/webp .webp
AddType image/x-icon .cur .ico
# Media Files
AddType application/x-shockwave-flash .swf
AddType audio/ogg .oga .ogg .opus
AddType video/ogg .ogv
# Script Files
AddType application/javascript .js
# Style Files
AddType text/css .css
# Text Files
AddType application/pdf .pdf
AddType text/plain .txt
# Other Files
AddType application/octet-stream .bin .exe
###########################
# SECURITY - CROSS-ORIGIN #
###########################
<FilesMatch "\.(?:bmp|cur|gif|ico|jp(?:eg?|g)|png|svgz?|tiff?|webp)$">
SetEnvIf Origin ":" is-cors
Header set Access-Control-Allow-Origin "*" env=is-cors
</FilesMatch>
<FilesMatch "\.(?:eot|otf|tt[cf]|woff2?)$">
Header set Access-Control-Allow-Origin "*"
</FilesMatch>
#####################
# SECURITY - ERRORS #
#####################
ErrorDocument 400 /error/400/
ErrorDocument 401 /error/401/
ErrorDocument 402 /error/402/
ErrorDocument 403 /error/403/
ErrorDocument 404 /error/404/
ErrorDocument 405 /error/405/
ErrorDocument 406 /error/406/
ErrorDocument 407 /error/407/
ErrorDocument 408 /error/408/
ErrorDocument 409 /error/409/
ErrorDocument 410 /error/410/
ErrorDocument 411 /error/411/
ErrorDocument 412 /error/412/
ErrorDocument 413 /error/413/
ErrorDocument 414 /error/414/
ErrorDocument 415 /error/415/
ErrorDocument 416 /error/416/
ErrorDocument 417 /error/417/
ErrorDocument 422 /error/422/
ErrorDocument 423 /error/423/
ErrorDocument 424 /error/424/
ErrorDocument 426 /error/426/
ErrorDocument 428 /error/428/
ErrorDocument 429 /error/429/
ErrorDocument 431 /error/431/
ErrorDocument 500 /error/500/
ErrorDocument 501 /error/501/
ErrorDocument 502 /error/502/
ErrorDocument 503 /error/503/
ErrorDocument 504 /error/504/
ErrorDocument 505 /error/505/
ErrorDocument 506 /error/506/
ErrorDocument 507 /error/507/
ErrorDocument 508 /error/508/
ErrorDocument 510 /error/510/
ErrorDocument 511 /error/511/
############################
# SECURITY - MISCELLANEOUS #
############################
Header set X-Content-Type-Options "nosniff"
Header unset Server
Header unset X-Powered-By
LimitRequestBody 1048576
ServerSignature Off
############
# REWRITES #
############
# Settings
RewriteEngine On
RewriteBase /
# Security: Protected Resources
RewriteCond %{REQUEST_FILENAME} -d [OR]
RewriteCond %{REQUEST_FILENAME} -f
RewriteCond %{REQUEST_URI} (?:/|^)\. [OR]
RewriteCond %{REQUEST_URI} \.(?:ba?k|cfg|conf|dist|fla|in[ci](?:\.php)?|info|log|po|psd|sh|sql|sw[op])$ [NC,OR]
RewriteCond %{REQUEST_URI} ^#.*#$ [OR]
RewriteCond %{REQUEST_URI} ~$
RewriteRule ^ - [F,L]
# Security: Vulnerabilities
RewriteCond %{REQUEST_METHOD} (?:DELETE|DEBUG|HEAD|TRACE|TRACK) [NC,OR]
RewriteCond %{THE_REQUEST} (?:\\n|\\r|%00|%0A|%0D|cgi-bin|etc/passwd) [NC,OR]
RewriteCond %{THE_REQUEST} (?:/\*|\?)\ HTTP/ [NC,OR]
RewriteCond %{QUERY_STRING} (?:/self/|cPath=) [NC,OR]
RewriteCond %{QUERY_STRING} (?:127\.0\.0\.1|localhost|loopback) [NC,OR]
RewriteCond %{QUERY_STRING} (?:<|%3C).*(?:embed|iframe|object|script|(?:[^e]*e)+mbed|(?:[^i]*i)+frame|(?:[^o]*o)+bject|(?:[^s]*s)+cript).*(?:>|%3E) [NC,OR]
RewriteCond %{QUERY_STRING} (?:\.{1,}/)+(?:bin|etc|motd) [NC,OR]
RewriteCond %{QUERY_STRING} (?:\.|%2E)(?:\.|%2E)(?:/|%2F) [NC,OR]
RewriteCond %{QUERY_STRING} (?:\\n|\\r|%00|%0A|%0D) [NC,OR]
RewriteCond %{QUERY_STRING} (?:BASE64_(?:EN|DE)CODE|LOAD_FILE|MD5|OUTFILE|SHA1|SP_EXECUTESQL) [NC,OR]
RewriteCond %{QUERY_STRING} (?:CONCAT[^\(]*\(|UNION(?:(?:[^A]*A)+LL)?(?:[^S]*S)+ELECT) [NC,OR]
RewriteCond %{QUERY_STRING} (?:ftp|https?)(?::|%3A)(?:/|%2F)?(?:/|%2F)? [NC,OR]
RewriteCond %{QUERY_STRING} (?:GLOBALS|_REQUEST)(?:=|\[|\%[a-z0-9]{0,2}) [NC,OR]
RewriteCond %{QUERY_STRING} =\|w\| [NC,OR]
RewriteCond %{QUERY_STRING} =PHP[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12} [NC,OR]
RewriteCond %{QUERY_STRING} [a-z0-9_]=(?:(?:\.\.//?)+|/(?:[a-z0-9._]//?)$) [NC,OR]
RewriteCond %{QUERY_STRING} mosConfig_[a-z_]{1,21}(?:=|%3D) [NC,OR]
RewriteCond %{QUERY_STRING} proc/self/environ [NC]
RewriteRule ^ - [F,L]
# Cosmetics: Remove WWW Subdomain
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteCond %{HTTPS}s ^on(s)|
RewriteRule ^ http%2://%1%{REQUEST_URI} [R=301,L]
# Cosmetics: Remove Trailing Slashes
RewriteCond %{THE_REQUEST} //
RewriteRule .* 0ドル [R=301,L]
# Cosmetics: Remove PHP Extension
RewriteCond %{ENV:REDIRECT_STATUS} !(?:^4|^5)
RewriteCond %{THE_REQUEST} ^[A-Z]{3,}\ .*\.php
RewriteRule ^(.*)\.php /1ドル/ [R=301,L]
# Cosmetics: Remove Index
RewriteRule ^(.*)index/$ /1ドル [R=301,L]
# Cosmetics: Force Trailing Slash
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule .*[^/]$ 0ドル/ [R=301,L]
# Functions: Dynamic Versioning
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.+)\.[0-9]+\.(bmp|css|cur|gif|ico|jp(?:eg?|g)|js|png|svgz?|tiff?|webp)$ 1ドル.2ドル [L]
# Functions: Pretty URLs
RewriteRule ^error/([^/]+)/$ /error.php?code=1ドル [L]
RewriteRule ^threads/([0-9]+)/$ /thread.php?id=1ドル [L]
RewriteRule ^users/([0-9]+)/$ /users.php?id=1ドル [L]
# Functions: Resources Lookup
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteCond %{REQUEST_URI} /.*/$
RewriteRule ^(.*)/$ 1ドル\.php [L]
1 Answer 1
This is not a full answer, as I'm not that guru about Apache (and your .htaccess
covers a lot of cases).
But merely regarding your error.php
script, I suggest a reduced code, improving maintainability:
$messages = [
400 => 'Bad Request',
401 => 'Unauthorized',
402 => 'Payment Required',
403 => 'Forbidden',
404 => 'Not Found',
405 => 'Method Not Allowed',
406 => 'Not Acceptable',
407 => 'Proxy Authentication Required',
408 => 'Request Timeout',
409 => 'Conflict',
410 => 'Gone',
411 => 'Length Required',
412 => 'Precondition Failed',
413 => 'Request Entity Too Large',
414 => 'Request-URI Too Long',
415 => 'Unsupported Media Type',
416 => 'Requested Range Not Satisfiable',
417 => 'Expectation Failed',
418 => 'I\'m a Teapot',
421 => 'Misdirected Request',
422 => 'Unprocessable Entity',
423 => 'Locked',
424 => 'Failed Dependency',
425 => 'Unordered Collection',
426 => 'Upgrade Required',
428 => 'Precondition Required',
429 => 'Too Many Requests',
431 => 'Request Header Fields Too Large',
451 => 'Unavailable For Legal Reasons',
500 => 'Internal Server Error',
501 => 'Not Implemented',
502 => 'Bad Gateway',
503 => 'Service Unavailable',
504 => 'Gateway Timeout',
505 => 'HTTP Version Not Supported',
506 => 'Variant Also Negotiates',
507 => 'Insufficient Storage',
508 => 'Loop Detected',
509 => 'Bandwidth Limit Exceeded',
510 => 'Not Extended',
511 => 'Network Authentication Required',
'???' => 'Unknown Error',
];
array_key_exists(
($code =
@$_GET['code'] ? $_GET['code'] : (
@$_SERVER['REDIRECT_STATUS'] ? $_SERVER['REDIRECT_STATUS'] : 0)
), $messages ? $code : '???'
);
echo $code . ' ' . $messages[$code];
You may note the use of @
: I know there is an eternal controversy about it, but I'm firmly partisan of using it in a few clearly identified cases such this one (i.e. when the only "error" which may happen and be masked is just the equivalent of isset()
).