askiiart-net/blog/300.md
2024-03-06 23:42:03 -06:00

96 lines
5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 300
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.
---
Since I have my code 1) [on my Gitea (now Forgejo) instance](https://git.askiiart.net/askiiart/askiiart-net), and 2) [mirrored](https://github.com/askiiart/askiiart.github.io) on GitHub, I didn't just want to set up a simple [308 Permanent Redirect](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/308) from [/.git](/.git) 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!
## ok but how?
300 seems great, but there's no standardized way to use it. The number, and the `Location` header as the preferred choice is all that's actually standardized. Straight from [RFC 9110](https://httpwg.org/specs/rfc9110.html#status.300):
> If the server has a preferred choice, the server *SHOULD* generate a Location header field containing a preferred choice's URI reference. The user agent MAY use the Location field value for automatic redirection.
>
> For request methods other than HEAD, the server *SHOULD* 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.
Does this mean to return some HTML? A JSON dict? YAML? An image of a pickle with that info overlaid on top of it!? *Nobody knows!* And from the Mozilla docs:
> As there is no standardized way of choosing one of the responses, this response code is very rarely used.
Very helpful.
## ok but actually how?
After a bit of Googling, I found [this Stack Overflow thread](https://stackoverflow.com/questions/8905545/what-is-the-exact-response-structure-for-http-status-code-300-multiple-choices), which just says this<sup>[<a href="#browser-specific-info">note</a>]</sup>:
> The "multiple choices" are done by sending the links in hypertext (HTML) content and let the *user* pick.
Well, that's boring. Guess I'll do that then.
```language-nginx
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>';
}
```
## nginx is hard
That config looks fine, right? It just sets the `Location` header and returns this basic menu to pick from. But it won't open in any browsers, Firefox displays nothing, and has `NS_ERROR_WONT_HANDLE_CONTENT` in the developer tools, and Chrome shows a `ERR_INVALID_RESPONSE` error. Running `curl -I https://askiiart.net`, and...
```sh
HTTP/1.1 300
Server: nginx/1.25.3
Date: Wed, 06 Dec 2023 16:36:01 GMT
Content-Type: application/octet-stream
Content-Length: 223
Connection: keep-alive
```
There's no Location header there! And the browsers are probably refusing to show it because of the wrong `Content-Type` and no, adding a header with `add_header` doesn't work for that one, either.
Turns out in order to be able to use `add_header`, you first need to set `default_type` (which sets a default `Content-Type`). I could've set that in the `location /.git` block, but I just decided to make it global.
So here's the fixed config:
```language-nginx
# 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>';
}
```
Yep, that works! I had set up my IP address pages the same way, so let's fix that:
```language-nginx
location /ip {
default_type text/plain;
return 200 "$remote_addr";
}
location /ip/json {
default_type application/json;
return 200 '{"ip":"$remote_addr"}';
}
```
These still don't work, they just return my reverse proxy container's gateway's address, but that's for another time.
## Open your eyes... wake up, *Link*
Turns out might be a standard way to do it! From [RFC 9110](https://httpwg.org/specs/rfc9110.html#status.300), again:
> It is possible to communicate the list using a set of Link header fields [RFC5988](https://httpwg.org/specs/rfc9110.html#RFC8288), each with a relationship of "alternate", though deployment is a chicken-and-egg problem.
I'll probably figure out the `Link` header some other time and will add this later, at which point I'll post an addendum. Until then, bye I guess!
## Footnotes and stuff
#### browser-specific info
Elsewhere in the Quora thread it says, in short, that Firefox and Chrome just displays the HTML, and Safari will follow the `Location` header. Edit: Apparently I forgot to link the Quora thread, so I've got no idea what I'm referencing here. Sorry! \[[Back](#ok-but-actually-how)\]