Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,16 @@ add_filter('post_content_to_markdown/post_allowed', function ($allowed, $post) {

**Default:** `true`

#### `post_content_to_markdown/strict_accept`

Controls whether the plugin returns `406 Not Acceptable` when the client's `Accept` header rules out every representation the site can serve (i.e. neither `text/html` nor `text/markdown` is acceptable). Enabled by default for spec-correct content negotiation. Disable if you'd rather always fall back to HTML for any `Accept` value.

```php
add_filter('post_content_to_markdown/strict_accept', '__return_false');
```

**Default:** `true`

#### `post_content_to_markdown/emit_vary`

Controls whether the plugin appends `Vary: Accept` to every front-end response. Enabled by default so that downstream caches (browsers, proxies, CDNs) key on the Accept header and don't cross-serve an HTML response to a Markdown request (or vice versa). Disable if you don't want the plugin touching HTML response headers — Markdown responses will still include `Vary: Accept` regardless, so content negotiation stays correct for direct Markdown hits.
Expand Down
41 changes: 41 additions & 0 deletions post-content-to-markdown.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,22 @@ function isMarkdownRequested()
return $markdown > 0.0 && $markdown > $html;
}

/**
* Whether the client's Accept header allows any representation we can serve
* (text/html or text/markdown). Missing Accept counts as acceptable per
* RFC 9110 (absence means any type is acceptable).
*/
function isAcceptable()
{
$accept = $_SERVER['HTTP_ACCEPT'] ?? '';
if ($accept === '') {
return true;
}

return acceptQuality($accept, 'text', 'html') > 0.0
|| acceptQuality($accept, 'text', 'markdown') > 0.0;
}

/**
* Tell caching plugins (WP Super Cache, etc.) to skip caching for markdown requests.
* This runs early to ensure the constant is set before caching plugins check it.
Expand Down Expand Up @@ -118,6 +134,31 @@ function isMarkdownRequested()
header('Vary: Accept', false);
});

/**
* Return 406 Not Acceptable when the client's Accept header rules out every
* representation we can serve. Disable via the strict_accept filter to fall
* back to HTML instead.
*/
add_action('template_redirect', function () {
if (is_feed() || is_admin()) {
return;
}

if (! apply_filters('post_content_to_markdown/strict_accept', true)) {
return;
}

if (isAcceptable()) {
return;
}

status_header(406);
header('Content-Type: text/plain; charset=utf-8');
header('Vary: Accept', false);
echo "406 Not Acceptable\n\nAvailable representations: text/html, text/markdown\n";
exit;
}, 1);

/**
* Serve content as Markdown based on Accept header or query parameter
*/
Expand Down
Loading