[Bug]: RSS feed serves m4b files with Content-Type: application/octet-stream instead of audio/mp4 #3223

Open
opened 2026-02-20 11:01:34 -05:00 by deekerman · 0 comments
Owner

Originally created by @jordwil on GitHub (Feb 8, 2026).

What happened?

When serving audiobook files via the RSS feed endpoint (/feed/:slug/item/:episodeId/*), .m4b files are served with Content-Type: application/octet-stream instead of audio/mp4.

This is because RssFeedManager.getFeedItem() in server/managers/RssFeedManager.js uses res.sendFile(episodePath) without setting the Content-Type header. Express's sendFile relies on the mime package for content-type lookup, and .m4b is not in the standard MIME database, so it falls back to application/octet-stream.

This causes slow playback start (10-20+ seconds) in podcast clients like Apple Podcasts when consuming audiobook RSS feeds, because the client cannot identify the audio format from the response headers and must sniff the file content instead.

This is the same underlying issue that was fixed for the download endpoint in PR #3319 (issue #3310), but the RSS feed file-serving codepath was not included in that fix.

Root cause

In server/managers/RssFeedManager.js line ~192:

async getFeedItem(req, res) {
    // ...
    res.sendFile(episodePath)  // No Content-Type set — falls back to application/octet-stream for .m4b
}

Compare with the download endpoint in server/controllers/LibraryItemController.js which correctly uses getAudioMimeTypeFromExtname():

const audioMimeType = getAudioMimeTypeFromExtname(Path.extname(libraryFile.metadata.path))
if (audioMimeType) {
    res.setHeader('Content-Type', audioMimeType)
}

Also compare with getFeedCover() in the same RssFeedManager.js file, which correctly sets the content type:

const extname = Path.extname(feed.coverPath).toLowerCase().slice(1)
res.type(`image/${extname}`)

Suggested fix

Use getAudioMimeTypeFromExtname() (from server/utils/fileUtils.js) in getFeedItem() to set the correct Content-Type before calling res.sendFile():

const { getAudioMimeTypeFromExtname } = require('../utils/fileUtils')

async getFeedItem(req, res) {
    // ... existing feed/episode lookup ...
    const audioMimeType = getAudioMimeTypeFromExtname(Path.extname(episodePath))
    if (audioMimeType) {
        res.setHeader('Content-Type', audioMimeType)
    }
    res.sendFile(episodePath)
}

The AudioMimeType map in server/utils/constants.js already correctly maps .m4baudio/mp4.

What did you expect to happen?

RSS feed episode files should be served with the correct Content-Type: audio/mp4 header for .m4b files (and correct MIME types for other audio formats), matching the behavior of the download endpoint.

Steps to reproduce the issue

  1. Create an audiobook library with .m4b files
  2. Open an RSS feed for the audiobook
  3. Subscribe to the feed in Apple Podcasts (or any podcast client)
  4. Observe slow playback start times
  5. Inspect the HTTP response headers for a feed episode URL — Content-Type will be application/octet-stream

Verify with curl:

curl -sI "https://your-server/feed/your-slug/item/episode-id/media.m4b" | grep -i content-type
# Returns: Content-Type: application/octet-stream
# Expected: Content-Type: audio/mp4

Audiobookshelf version

v2.32.1

How are you running audiobookshelf?

Docker

What OS is your Audiobookshelf server hosted from?

Linux

Logs

No response

Additional Notes

Workaround: If using a reverse proxy (e.g., Caddy, nginx), you can rewrite the Content-Type header at the proxy level:

Caddy example:

reverse_proxy audiobookshelf:80 {
    header_down Content-Type application/octet-stream audio/mp4
}

Note: this workaround replaces application/octet-stream with audio/mp4 for all responses from ABS, which works if your ABS instance primarily serves audiobooks. A server-side fix is more appropriate.

This issue affects all audio formats not in the standard mime package lookup table, but .m4b is the most common case since it's the standard audiobook format.

Originally created by @jordwil on GitHub (Feb 8, 2026). ### What happened? When serving audiobook files via the RSS feed endpoint (`/feed/:slug/item/:episodeId/*`), `.m4b` files are served with `Content-Type: application/octet-stream` instead of `audio/mp4`. This is because `RssFeedManager.getFeedItem()` in `server/managers/RssFeedManager.js` uses `res.sendFile(episodePath)` without setting the Content-Type header. Express's `sendFile` relies on the `mime` package for content-type lookup, and `.m4b` is not in the standard MIME database, so it falls back to `application/octet-stream`. This causes **slow playback start (10-20+ seconds)** in podcast clients like Apple Podcasts when consuming audiobook RSS feeds, because the client cannot identify the audio format from the response headers and must sniff the file content instead. **This is the same underlying issue that was fixed for the download endpoint in PR #3319 (issue #3310)**, but the RSS feed file-serving codepath was not included in that fix. #### Root cause In [`server/managers/RssFeedManager.js` line ~192](https://github.com/advplyr/audiobookshelf/blob/master/server/managers/RssFeedManager.js#L192): ```javascript async getFeedItem(req, res) { // ... res.sendFile(episodePath) // No Content-Type set — falls back to application/octet-stream for .m4b } ``` Compare with the download endpoint in `server/controllers/LibraryItemController.js` which correctly uses `getAudioMimeTypeFromExtname()`: ```javascript const audioMimeType = getAudioMimeTypeFromExtname(Path.extname(libraryFile.metadata.path)) if (audioMimeType) { res.setHeader('Content-Type', audioMimeType) } ``` Also compare with `getFeedCover()` in the same `RssFeedManager.js` file, which correctly sets the content type: ```javascript const extname = Path.extname(feed.coverPath).toLowerCase().slice(1) res.type(`image/${extname}`) ``` #### Suggested fix Use `getAudioMimeTypeFromExtname()` (from `server/utils/fileUtils.js`) in `getFeedItem()` to set the correct Content-Type before calling `res.sendFile()`: ```javascript const { getAudioMimeTypeFromExtname } = require('../utils/fileUtils') async getFeedItem(req, res) { // ... existing feed/episode lookup ... const audioMimeType = getAudioMimeTypeFromExtname(Path.extname(episodePath)) if (audioMimeType) { res.setHeader('Content-Type', audioMimeType) } res.sendFile(episodePath) } ``` The `AudioMimeType` map in `server/utils/constants.js` already correctly maps `.m4b` → `audio/mp4`. ### What did you expect to happen? RSS feed episode files should be served with the correct `Content-Type: audio/mp4` header for `.m4b` files (and correct MIME types for other audio formats), matching the behavior of the download endpoint. ### Steps to reproduce the issue 1. Create an audiobook library with `.m4b` files 2. Open an RSS feed for the audiobook 3. Subscribe to the feed in Apple Podcasts (or any podcast client) 4. Observe slow playback start times 5. Inspect the HTTP response headers for a feed episode URL — `Content-Type` will be `application/octet-stream` Verify with curl: ```bash curl -sI "https://your-server/feed/your-slug/item/episode-id/media.m4b" | grep -i content-type # Returns: Content-Type: application/octet-stream # Expected: Content-Type: audio/mp4 ``` ### Audiobookshelf version v2.32.1 ### How are you running audiobookshelf? Docker ### What OS is your Audiobookshelf server hosted from? Linux ### Logs _No response_ ### Additional Notes **Workaround:** If using a reverse proxy (e.g., Caddy, nginx), you can rewrite the Content-Type header at the proxy level: Caddy example: ``` reverse_proxy audiobookshelf:80 { header_down Content-Type application/octet-stream audio/mp4 } ``` Note: this workaround replaces `application/octet-stream` with `audio/mp4` for all responses from ABS, which works if your ABS instance primarily serves audiobooks. A server-side fix is more appropriate. This issue affects all audio formats not in the standard `mime` package lookup table, but `.m4b` is the most common case since it's the standard audiobook format.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/audiobookshelf-advplyr#3223
No description provided.