From e2b9bd5b64d6888f114847c33021282c823f16b6 Mon Sep 17 00:00:00 2001 From: "Zack M. Davis" Date: Tue, 21 Jul 2026 20:26:50 -0700 Subject: [PATCH] support Markdown content negotiation (and consolidate conf.d file convention) --- .../aal_month_map.conf} | 2 +- .../common_expires_map.conf} | 5 ++- .../conf.d/markdown_negotiation_map.conf | 40 +++++++++++++++++++ provisioning/nginx_siteconf | 39 ++++++++++++++++-- 4 files changed, 80 insertions(+), 6 deletions(-) rename provisioning/{nginx_aal_month_map.conf => conf.d/aal_month_map.conf} (94%) rename provisioning/{nginx_common_expires_map.conf => conf.d/common_expires_map.conf} (77%) create mode 100644 provisioning/conf.d/markdown_negotiation_map.conf diff --git a/provisioning/nginx_aal_month_map.conf b/provisioning/conf.d/aal_month_map.conf similarity index 94% rename from provisioning/nginx_aal_month_map.conf rename to provisioning/conf.d/aal_month_map.conf index 4320306..6ce64ae 100644 --- a/provisioning/nginx_aal_month_map.conf +++ b/provisioning/conf.d/aal_month_map.conf @@ -5,7 +5,7 @@ # 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, +# 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. diff --git a/provisioning/nginx_common_expires_map.conf b/provisioning/conf.d/common_expires_map.conf similarity index 77% rename from provisioning/nginx_common_expires_map.conf rename to provisioning/conf.d/common_expires_map.conf index a0b2eca..de18dc8 100644 --- a/provisioning/nginx_common_expires_map.conf +++ b/provisioning/conf.d/common_expires_map.conf @@ -1,8 +1,9 @@ # 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. +# 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 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_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 { -- 2.53.0