95 lines
4.9 KiB
Markdown
95 lines
4.9 KiB
Markdown
# 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 self-hosted Gitea (soon 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-net">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. \[[Back](#ok-but-actually-how)\]
|