From: Zack M. Davis Date: Wed, 22 Jul 2026 03:26:50 +0000 (-0700) Subject: support Markdown content negotiation (and consolidate conf.d file convention) X-Git-Url: https://zackmdavis.net/blog/source?a=commitdiff_plain;h=e2b9bd5b64d6888f114847c33021282c823f16b6;p=An_Algorithmic_Lucidity.git support Markdown content negotiation (and consolidate conf.d file convention) --- diff --git a/provisioning/conf.d/aal_month_map.conf b/provisioning/conf.d/aal_month_map.conf new file mode 100644 index 0000000..6ce64ae --- /dev/null +++ b/provisioning/conf.d/aal_month_map.conf @@ -0,0 +1,40 @@ +# Translates the old WordPress site's two-digit month URLs +# (/blog/2011/12/some-post/) onto Pelican's %b abbreviation +# (/blog/2011/Dec/some-post/), matching ARTICLE_URL / MONTH_ARCHIVE_SAVE_AS +# in pelicanconf.py. Referenced from the "location ~ ^/blog/..." redirect +# block in nginx_siteconf. +# +# This is an aAL-specific legacy-URL concern (WordPress's permalink +# structure), not a generally-shared one like common_expires_map.conf, +# so the map name is namespaced ($aal_month_abbrev) rather than generic -- +# no reason another blog sharing this server would need an identically-named +# map, but no reason to risk a collision either. +# +# Included once from nginx.conf's http{} block (map directives can't live +# inside a server{} block). +# +# Keyed on $2 (the location regex's second capture group in nginx_siteconf), +# not a named capture -- conf.d/*.conf loads before sites-enabled/* in +# nginx.conf, so at the point this map is parsed, nginx doesn't yet know +# about a named capture a later location block would define. $1-$9 are +# built-in nginx variables, always predeclared, so they don't have that +# ordering problem. +map $2 $aal_month_abbrev { + # Passes unrecognized input through unchanged, so a URL with a + # malformed month degrades to an ordinary 404 rather than a redirect to + # a broken location. + default $2; + + 01 Jan; + 02 Feb; + 03 Mar; + 04 Apr; + 05 May; + 06 Jun; + 07 Jul; + 08 Aug; + 09 Sep; + 10 Oct; + 11 Nov; + 12 Dec; +} diff --git a/provisioning/conf.d/common_expires_map.conf b/provisioning/conf.d/common_expires_map.conf new file mode 100644 index 0000000..de18dc8 --- /dev/null +++ b/provisioning/conf.d/common_expires_map.conf @@ -0,0 +1,14 @@ +# Shared across all sites on a box (nginx errors on a duplicate `map` name +# within the same http{} context) -- included once, from nginx.conf's http{} +# block, not from any individual site's config. Kept as an identical copy in +# each blog's own provisioning/conf.d/ so each repo stays independently +# rebuildable; only one of those copies is the one actually `include`d on a +# given server. +# +# Forces HTML responses to always revalidate (since a post's URL stays the +# same across edits/redeploys, so a cached page could otherwise go stale +# silently) while leaving normal caching behavior for everything else. +map $sent_http_content_type $expires { + default off; + text/html epoch; +} diff --git a/provisioning/conf.d/markdown_negotiation_map.conf b/provisioning/conf.d/markdown_negotiation_map.conf new file mode 100644 index 0000000..70048f9 --- /dev/null +++ b/provisioning/conf.d/markdown_negotiation_map.conf @@ -0,0 +1,40 @@ +# Supports the Markdown-alternative content negotiation in the "location +# /blog/" block of nginx_siteconf: if a client's Accept header asks for +# text/markdown (or text/plain) and a page has a sibling .md mirror (see +# _prepare_markdown_mirrors/_write_markdown_mirrors in pelicanconf.py), serve +# that instead of the compiled HTML. Prior art: gwern.net does the same +# thing for the same reason (LLM-agent support) -- +# . +# +# This is an aAL-specific concern, not a generally-shared one like +# common_expires_map.conf, so map names are namespaced ($aal_...) rather +# than generic. +# +# Included once from nginx.conf's http{} block (map directives can't live +# inside a server{} block). + +# Article URLs are directories (ARTICLE_URL ends in "/" -- see +# pelicanconf.py), but a page's markdown_url is its directory's *sibling* +# with no trailing slash (dirname(save_as) + '.md'), so the trailing slash +# has to come off before appending ".md". Static-asset URLs (no trailing +# slash to begin with) pass through unchanged. +map $uri $aal_uri_sans_trailing_slash { + "~^(?.*)/$" $base; + default $uri; +} + +# gwern.net's version uses `if`, which requires unrolling since nginx +# disallows nested `if`s inside a location. We can dodge `if` (and the +# well-known "IfIsEvil" footguns that come with mixing it into +# try_files-driven logic) entirely: fold "does the client want markdown" into +# a string that's either ".md" or empty, and let try_files' own +# first-candidate-that-exists-wins semantics (already relied on elsewhere in +# nginx_siteconf) do the branching. When a client doesn't ask for markdown, +# this suffix is empty and the resulting candidate is byte-for-byte identical +# to the plain $uri candidate already tried today -- so behavior for +# ordinary browser traffic is provably unchanged. +map $http_accept $aal_wants_markdown_suffix { + default ""; + "~*text/markdown" ".md"; + "~*text/plain" ".md"; +} diff --git a/provisioning/nginx_aal_month_map.conf b/provisioning/nginx_aal_month_map.conf deleted file mode 100644 index 4320306..0000000 --- a/provisioning/nginx_aal_month_map.conf +++ /dev/null @@ -1,40 +0,0 @@ -# Translates the old WordPress site's two-digit month URLs -# (/blog/2011/12/some-post/) onto Pelican's %b abbreviation -# (/blog/2011/Dec/some-post/), matching ARTICLE_URL / MONTH_ARCHIVE_SAVE_AS -# in pelicanconf.py. Referenced from the "location ~ ^/blog/..." redirect -# block in nginx_siteconf. -# -# This is an aAL-specific legacy-URL concern (WordPress's permalink -# structure), not a generally-shared one like nginx_common_expires_map.conf, -# so the map name is namespaced ($aal_month_abbrev) rather than generic -- -# no reason another blog sharing this server would need an identically-named -# map, but no reason to risk a collision either. -# -# Included once from nginx.conf's http{} block (map directives can't live -# inside a server{} block). -# -# Keyed on $2 (the location regex's second capture group in nginx_siteconf), -# not a named capture -- conf.d/*.conf loads before sites-enabled/* in -# nginx.conf, so at the point this map is parsed, nginx doesn't yet know -# about a named capture a later location block would define. $1-$9 are -# built-in nginx variables, always predeclared, so they don't have that -# ordering problem. -map $2 $aal_month_abbrev { - # Passes unrecognized input through unchanged, so a URL with a - # malformed month degrades to an ordinary 404 rather than a redirect to - # a broken location. - default $2; - - 01 Jan; - 02 Feb; - 03 Mar; - 04 Apr; - 05 May; - 06 Jun; - 07 Jul; - 08 Aug; - 09 Sep; - 10 Oct; - 11 Nov; - 12 Dec; -} diff --git a/provisioning/nginx_common_expires_map.conf b/provisioning/nginx_common_expires_map.conf deleted file mode 100644 index a0b2eca..0000000 --- a/provisioning/nginx_common_expires_map.conf +++ /dev/null @@ -1,13 +0,0 @@ -# Shared across all sites on a box (nginx errors on a duplicate `map` name -# within the same http{} context) -- included once, from nginx.conf's http{} -# block, not from any individual site's config. Kept as an identical copy in -# each blog's own provisioning/ so each repo stays independently rebuildable; -# only one of those copies is the one actually `include`d on a given server. -# -# Forces HTML responses to always revalidate (since a post's URL stays the -# same across edits/redeploys, so a cached page could otherwise go stale -# silently) while leaving normal caching behavior for everything else. -map $sent_http_content_type $expires { - default off; - text/html epoch; -} diff --git a/provisioning/nginx_siteconf b/provisioning/nginx_siteconf index 494c505..190ae54 100644 --- a/provisioning/nginx_siteconf +++ b/provisioning/nginx_siteconf @@ -1,5 +1,5 @@ -# $expires is defined by nginx_common_expires_map.conf, and $aal_month_abbrev -# by nginx_aal_month_map.conf, both included once from nginx.conf's http{} +# $expires is defined by conf.d/common_expires_map.conf, and $aal_month_abbrev +# by conf.d/aal_month_map.conf, both included once from nginx.conf's http{} # block (not here -- see those files for why). # # All of the redirects below are 302 (not 301) for now, deliberately: we're @@ -104,7 +104,40 @@ server { location /blog/ { alias /home/blogmistress/An_Algorithmic_Lucidity/output/; index index.html index.xml; - try_files $uri $uri/ =404; + + # .md isn't in nginx's default mime.types, so it's currently + # served as application/octet-stream (forcing a download prompt + # instead of an inline view). Fix belongs in mime.types itself + # (one line, `text/markdown md;`, inside its existing `types {}` + # block) rather than a location-scoped default_type override: + # default_type is a last-resort fallback for the whole location, + # so overriding it here would silently relabel every OTHER + # currently-or-future unrecognized extension served from + # /blog/ (e.g. llms.txt, from _write_llms_txt in + # pelicanconf.py) as text/markdown too, not just .md files. + # mime.types is OS-managed, not part of this repo, so this edit + # has to happen directly on the server. + + # See conf.d/markdown_negotiation_map.conf for + # $aal_uri_sans_trailing_slash and $aal_wants_markdown_suffix. + # + # This candidate MUST come before plain $uri, not after: for any + # article URL (always directory-shaped, trailing slash and all -- + # see ARTICLE_URL in pelicanconf.py), $uri checks as an existing + # *directory* (try_files treats a trailing slash as "check + # directory existence") and that directory always exists, so + # $uri would win the match immediately, every time, if tried + # first -- and try_files stops at the first match, with no way + # to keep looking for something better. Putting the markdown + # candidate first is what gives it a chance to fire at all. + # + # When a client doesn't ask for markdown, this first candidate + # has no trailing slash, so it's checked as a *file* instead -- + # which a directory URL never is, so it harmlessly falls through + # to the plain $uri/$uri/ pair below, unchanged from before this + # feature existed. (For non-directory URLs, e.g. static assets, + # it collapses to a literal duplicate of $uri -- also harmless.) + try_files $aal_uri_sans_trailing_slash$aal_wants_markdown_suffix $uri $uri/ =404; } location /blog/source {