When the expiration date of MinIO links passes, It responds to an XML like this:
<Error>
<Code>AccessDenied</Code>
<Message>Request has expired</Message>
<Key>key-of-the-resource</Key>
<BucketName>bucket-name</BucketName>
<Resource>/path-to/teh-resource</Resource>
<RequestId>16FC78B1C6185XC7</RequestId>
<HostId>5d405266-91b9-XXXX-ae27-c48694f203d5</HostId>
</Error>
Is there any way to customize this page by some sort of configuration inside the MinIO? I didn't find any related config on their documents.
Other potential solutions:
Update
complete response headers:
$ curl <minio-url> -I
HTTP/2 403
date: Tue, 05 Jul 2022 12:51:13 GMT
content-length: 0
accept-ranges: bytes
content-security-policy: block-all-mixed-content
strict-transport-security: max-age=15724800; includeSubDomains
vary: Origin
vary: Accept-Encoding
x-amz-request-id: 16FEEFE391X98X88
x-content-type-options: nosniff
x-xss-protection: 1; mode=block
complete response:
$ curl <minio-url>
<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>AccessDenied</Code><Message>Request has expired</Message><Key>new_structure/7553257.jpg</Key><BucketName>storage</BucketName><Resource>/decodl-storage/new_structure/7553257.jpg</Resource><RequestId>16FEEFFB573XXXXC</RequestId><HostId>5d405266-91b9-xxxx-ae27-c48694f203d5</HostId></Error>
Assuming your 403 error returns with the Content-Type
header being set to text/xml
, you can transform this XML response to the HTML with the nginx using XSL Transformations. To do it you'll need the XSLT module, and you should be aware this module is not built by default, it should be installed additionally as a dynamic module (or enabled with the --with-http_xslt_module
configuration parameter when you build nginx from the sources).
After you install the module, you should specify the xslt_stylesheet
directive under the location used to proxy requests to the MinIO backend:
location ... {
xslt_stylesheet /path/to/error.xslt;
...
}
Here is an example of the XSLT file that can be used to transform the XML response you've showed in your question:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="UTF-8" />
<xsl:template match="/">
<xsl:text disable-output-escaping="yes"><!DOCTYPE html></xsl:text>
<html>
<head>
<title><xsl:value-of select="Error/Code"/></title>
</head>
<style type="text/css">
body {
height: 100vh;
margin: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
p {
font-weight: bold;
}
.itemvalue {
font-family: monospace, monospace;
font-weight: normal;
font-size: 1em;
}
</style>
<body>
<h1><xsl:value-of select="Error/Message"/></h1>
<p>Additional information:</p>
<table><tbody>
<xsl:for-each select="Error/*[not(name()='Code' or name()='Message')]">
<tr>
<td class="itemname"><xsl:value-of select="local-name()"/>:</td>
<td class="itemvalue"><xsl:value-of select="."/></td>
</tr>
</xsl:for-each>
</tbody></table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
The above file, being applied to the response sample, will give you the following:
You can style the output whatever you like. I think this question is not about web design (and I'm not a designer), however provided information should be enough to be an example that you can adapt to your needs.
Update
If your MinIO response comes with somethat different MIME content type, e.g. application/xml
, you'd need to add that content type to the list of MIME types processed by the XSLT module with the xslt_types
directive:
location ... {
xslt_types application/xml;
xslt_stylesheet /path/to/error.xslt;
...
}
Digging futher into the XSLT I finished up with somewhat different XSLT file. This one will transform only error messages containing Error
top level node, leaving any other response unchanged:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/Error">
<html>
<head>
<title><xsl:value-of select="./Code"/></title>
</head>
<style type="text/css">
/* custom CSS styles, see the previous example */
</style>
<body>
<h1><xsl:value-of select="./Message"/></h1>
<p>Additional information:</p>
<table><tbody>
<xsl:for-each select="./node()[not(self::Code or self::Message)]">
<tr>
<td class="itemname"><xsl:value-of select="local-name()"/>:</td>
<td class="itemvalue"><xsl:value-of select="."/></td>
</tr>
</xsl:for-each>
</tbody></table>
</body>
</html>
</xsl:template>
<xsl:template match="/node()[not(self::Error)]">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>