138 lines
8.4 KiB
HTML
138 lines
8.4 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta content="width=device-width, initial-scale=1" charset="utf-8" />
|
||
<link rel="icon" href="/assets/askiiart.avif" type="image/icon">
|
||
<title>300</title>
|
||
<link href="/style.css" type="text/css" rel="stylesheet" />
|
||
<link href="/prism.css" rel="stylesheet" />
|
||
</head>
|
||
<body class="line-numbers">
|
||
<h1 id="section">300</h1>
|
||
<p>No, not the movie. I'm a nerd who's spent half their day so
|
||
far configuring nginx, it should be obvious at this point.</p>
|
||
<hr />
|
||
<p>Since I have my code 1) <a
|
||
href="https://git.askiiart.net/askiiart/askiiart-net">on my
|
||
Gitea (now Forgejo) instance</a>, and 2) <a
|
||
href="https://github.com/askiiart/askiiart.github.io">mirrored</a>
|
||
on GitHub, I didn't just want to set up a simple <a
|
||
href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/308">308
|
||
Permanent Redirect</a> from <a href="/.git">/.git</a> to my code
|
||
in one place. I wanted users to be able to see both options,
|
||
both Gitea and GitHub, and apparently there's actually a thing
|
||
for that already, I can just use that!</p>
|
||
<h2 id="ok-but-how">ok but how?</h2>
|
||
<p>300 seems great, but there's no standardized way to use it.
|
||
The number, and the <code>Location</code> header as the
|
||
preferred choice is all that's actually standardized. Straight
|
||
from <a
|
||
href="https://httpwg.org/specs/rfc9110.html#status.300">RFC
|
||
9110</a>:</p>
|
||
<blockquote>
|
||
<p>If the server has a preferred choice, the server
|
||
<em>SHOULD</em> generate a Location header field containing a
|
||
preferred choice's URI reference. The user agent MAY use the
|
||
Location field value for automatic redirection.</p>
|
||
<p>For request methods other than HEAD, the server
|
||
<em>SHOULD</em> generate content in the 300 response containing
|
||
a list of representation metadata and URI reference(s) from
|
||
which the user or user agent can choose the one most
|
||
preferred.</p>
|
||
</blockquote>
|
||
<p>Does this mean to return some HTML? A JSON dict? YAML? An
|
||
image of a pickle with that info overlaid on top of it!?
|
||
<em>Nobody knows!</em> And from the Mozilla docs:</p>
|
||
<blockquote>
|
||
<p>As there is no standardized way of choosing one of the
|
||
responses, this response code is very rarely used.</p>
|
||
</blockquote>
|
||
<p>Very helpful.</p>
|
||
<h2 id="ok-but-actually-how">ok but actually how?</h2>
|
||
<p>After a bit of Googling, I found <a
|
||
href="https://stackoverflow.com/questions/8905545/what-is-the-exact-response-structure-for-http-status-code-300-multiple-choices">this
|
||
Stack Overflow thread</a>, which just says
|
||
this<sup>[<a href="#browser-specific-info">note</a>]</sup>:</p>
|
||
<blockquote>
|
||
<p>The "multiple choices" are done by sending the links in
|
||
hypertext (HTML) content and let the <em>user</em> pick.</p>
|
||
</blockquote>
|
||
<p>Well, that's boring. Guess I'll do that then.</p>
|
||
<pre class="language-nginx"><code> location /.git {
|
||
add_header Location "https://git.askiiart.net/askiiart/askiiart-net";
|
||
return 300 '<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"></head><body><a href="https://git.askiiart.net/askiiart/askiiart-net">Self-hosted</a><br><a href="https://github.com/askiiart/askiiart.github.io">GitHub</a></body></html>';
|
||
}</code></pre>
|
||
<h2 id="nginx-is-hard">nginx is hard</h2>
|
||
<p>That config looks fine, right? It just sets the
|
||
<code>Location</code> header and returns this basic menu to pick
|
||
from. But it won't open in any browsers, Firefox displays
|
||
nothing, and has <code>NS_ERROR_WONT_HANDLE_CONTENT</code> in
|
||
the developer tools, and Chrome shows a
|
||
<code>ERR_INVALID_RESPONSE</code> error. Running
|
||
<code>curl -I https://askiiart.net</code>, and...</p>
|
||
<div class="sourceCode" id="cb2"><pre
|
||
class="language-sh"><code class="language-bash"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="ex">HTTP/1.1</span> 300 </span>
|
||
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="ex">Server:</span> nginx/1.25.3</span>
|
||
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="ex">Date:</span> Wed, 06 Dec 2023 16:36:01 GMT</span>
|
||
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="ex">Content-Type:</span> application/octet-stream</span>
|
||
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a><span class="ex">Content-Length:</span> 223</span>
|
||
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a><span class="ex">Connection:</span> keep-alive</span></code></pre></div>
|
||
<p>There's no Location header there! And the browsers are
|
||
probably refusing to show it because of the wrong
|
||
<code>Content-Type</code> – and no, adding a header with
|
||
<code>add_header</code> doesn't work for that one, either.</p>
|
||
<p>Turns out in order to be able to use <code>add_header</code>,
|
||
you first need to set <code>default_type</code> (which sets a
|
||
default <code>Content-Type</code>). I could've set that in the
|
||
<code>location /.git</code> block, but I just decided to make it
|
||
global.</p>
|
||
<p>So here's the fixed config:</p>
|
||
<pre class="language-nginx"><code> # actually much higher in file
|
||
default_type "text/html";
|
||
|
||
location /.git {
|
||
add_header Location "https://git.askiiart.net/askiiart/askiiart-net";
|
||
return 300 '<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"></head><body><a href="https://git.askiiart.net/askiiart/askiiart-net">Self-hosted</a><br><a href="https://github.com/askiiart/askiiart-net">GitHub</a></body></html>';
|
||
}</code></pre>
|
||
<p>Yep, that works! I had set up my IP address pages the same
|
||
way, so let's fix that:</p>
|
||
<pre class="language-nginx"><code> location /ip {
|
||
default_type text/plain;
|
||
return 200 "$remote_addr";
|
||
}
|
||
|
||
location /ip/json {
|
||
default_type application/json;
|
||
return 200 '{"ip":"$remote_addr"}';
|
||
}</code></pre>
|
||
<p>These still don't work, they just return my reverse proxy
|
||
container's gateway's address, but that's for another time.</p>
|
||
<h2 id="open-your-eyes...-wake-up-link">Open your eyes... wake
|
||
up, <em>Link</em></h2>
|
||
<p>Turns out might be a standard way to do it! From <a
|
||
href="https://httpwg.org/specs/rfc9110.html#status.300">RFC
|
||
9110</a>, again:</p>
|
||
<blockquote>
|
||
<p>It is possible to communicate the list using a set of Link
|
||
header fields <a
|
||
href="https://httpwg.org/specs/rfc9110.html#RFC8288">RFC5988</a>,
|
||
each with a relationship of "alternate", though deployment is a
|
||
chicken-and-egg problem.</p>
|
||
</blockquote>
|
||
<p>I'll probably figure out the <code>Link</code> header some
|
||
other time and will add this later, at which point I'll post an
|
||
addendum. Until then, bye I guess!</p>
|
||
<h2 id="footnotes-and-stuff">Footnotes and stuff</h2>
|
||
<h4 id="browser-specific-info">browser-specific info</h4>
|
||
<p>Elsewhere in the Quora thread it says, in short, that Firefox
|
||
and Chrome just displays the HTML, and Safari will follow the
|
||
<code>Location</code> header. Edit: Apparently I forgot to link
|
||
the Quora thread, so I've got no idea what I'm referencing here.
|
||
Sorry! [<a href="#ok-but-actually-how">Back</a>]</p>
|
||
<iframe src="https://john.citrons.xyz/embed?ref=askiiart.net" style="margin-left:auto;display:block;margin-right:auto;max-width:732px;width:100%;height:94px;border:none;"></iframe>
|
||
<script src="/prism.js"></script>
|
||
</body>
|
||
<footer>
|
||
<p><a href="https://git.askiiart.net/askiiart/askiiart-net">code</a> (<a href="https://github.com/askiiart/askiiart.github.io">mirror</a>) <a href="/feed.xml">rss</a> <a href="/pubkey.asc">pubkey</a></p>
|
||
</footer>
|
||
</html>
|